From 52abf10f5c2d745491a168d1a5f1030368628b3f Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Fri, 12 Oct 2007 04:07:22 +0000 Subject: Bug 313123: Implement $component->create and $component->update based on Object.pm - Patch by Frédéric Buclin r/a=mkanat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/Bug.pm | 2 +- Bugzilla/Component.pm | 428 ++++++++++++++++++--- Bugzilla/Constants.pm | 4 + Bugzilla/WebService/Bug.pm | 7 +- Bugzilla/WebService/Constants.pm | 4 +- editcomponents.cgi | 275 ++----------- editflagtypes.cgi | 3 +- request.cgi | 4 +- .../en/default/admin/components/create.html.tmpl | 2 - .../en/default/admin/components/updated.html.tmpl | 46 +-- template/en/default/global/messages.html.tmpl | 6 + template/en/default/global/user-error.html.tmpl | 18 +- 12 files changed, 442 insertions(+), 357 deletions(-) diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index e318537a5..cc492ad41 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -930,7 +930,7 @@ sub _check_component { $name = trim($name); $name || ThrowUserError("require_component"); ($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; } diff --git a/Bugzilla/Component.pm b/Bugzilla/Component.pm index a615e7ae5..f5719e82c 100644 --- a/Bugzilla/Component.pm +++ b/Bugzilla/Component.pm @@ -23,10 +23,12 @@ package Bugzilla::Component; use base qw(Bugzilla::Object); +use Bugzilla::Constants; use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::User; use Bugzilla::FlagType; +use Bugzilla::Series; ############################### #### Initialization #### @@ -43,8 +45,32 @@ use constant DB_COLUMNS => qw( description ); -############################### -#### Methods #### +use constant REQUIRED_CREATE_FIELDS => qw( + 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 { @@ -79,6 +105,225 @@ sub new { 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 { my $self = shift; my $dbh = Bugzilla->dbh; @@ -143,15 +388,17 @@ sub flag_types { sub initial_cc { my $self = shift; - my $dbh = Bugzilla->dbh; if (!defined $self->{'initial_cc'}) { - my $cc_ids = $dbh->selectcol_arrayref( - "SELECT user_id FROM component_cc WHERE component_id = ?", - undef, $self->id); - my $initial_cc = Bugzilla::User->new_from_list($cc_ids); - $self->{'initial_cc'} = $initial_cc; + # If set_cc_list() has been called but data are not yet written + # into the DB, we want the new values defined by it. + my $cc_ids = $self->{cc_ids} + || $dbh->selectcol_arrayref('SELECT user_id FROM component_cc + WHERE component_id = ?', + undef, $self->id); + + $self->{'initial_cc'} = Bugzilla::User->new_from_list($cc_ids); } return $self->{'initial_cc'}; } @@ -178,27 +425,6 @@ sub product_id { return $_[0]->{'product_id'}; } #### 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; __END__ @@ -211,9 +437,8 @@ Bugzilla::Component - Bugzilla product component class. use Bugzilla::Component; - my $component = new Bugzilla::Component(1); - my $component = new Bugzilla::Component({product => $product, - name => 'AcmeComp'}); + my $component = new Bugzilla::Component($comp_id); + my $component = new Bugzilla::Component({ product => $product, name => $name }); my $bug_count = $component->bug_count(); my $bug_ids = $component->bug_ids(); @@ -228,7 +453,23 @@ Bugzilla::Component - Bugzilla product component class. my $bug_flag_types = $component->flag_types->{'bug'}; 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 @@ -241,14 +482,15 @@ Component.pm represents a Product Component object. =item C Description: The constructor is used to load an existing component - by passing a component id or a hash with the product - id and the component name. + by passing a component ID or a hash with the product + object the component belongs to and the component name. Params: $param - If you pass an integer, the integer is the - component id from the database that we want to - read in. If you pass in a hash with 'name' key, - then the value of the name key is the name of a - component from the DB. + component ID from the database that we want to + read in. If you pass in a hash with the 'name' + and 'product' keys, then the value of the name + key is the name of a component being in the given + product. Returns: A Bugzilla::Component object. @@ -288,41 +530,117 @@ Component.pm represents a Product Component object. =item C -Returns an arrayref of L objects representing the -Initial CC List. + Description: Returns a list of user objects representing users being + in the initial CC list. + + Params: none. + + Returns: An arrayref of L objects. =item C - Description: Returns all bug and attachment flagtypes available for - the component. + Description: Returns all bug and attachment flagtypes available for + 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 - 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 + + 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 + + Description: Changes the description of the component. + + Params: $new_desc - new description of the component (string). + + Returns: Nothing. + +=item C + + 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 + + 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 + + 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 + + Description: Write changes made to the component into the DB. + + Params: none. + + Returns: A hashref with changes made to the component object. + +=item C + + Description: Deletes the current component from the DB. The object itself + is not destroyed. + + Params: none. + + Returns: Nothing. =back -=head1 SUBROUTINES +=head1 CLASS METHODS =over -=item C +=item C - Description: Checks if the component name was passed in and if it is a valid - component. + Description: Create a new component for the given product. - Params: $product - A Bugzilla::Product object. - $comp_name - String with a component name. + Params: The hashref must have the following keys: + 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 diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 99edb2f50..f84ece006 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -147,6 +147,7 @@ use File::Basename; MAX_LEN_QUERY_NAME MAX_MILESTONE_SIZE + MAX_COMPONENT_SIZE ); @Bugzilla::Constants::EXPORT_OK = qw(contenttypes); @@ -410,6 +411,9 @@ use constant MAX_LEN_QUERY_NAME => 64; # The longest milestone name allowed. use constant MAX_MILESTONE_SIZE => 20; +# The longest component name allowed. +use constant MAX_COMPONENT_SIZE => 64; + sub bz_locations { # 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 diff --git a/Bugzilla/WebService/Bug.pm b/Bugzilla/WebService/Bug.pm index 14ed2e7fd..8dacfe956 100755 --- a/Bugzilla/WebService/Bug.pm +++ b/Bugzilla/WebService/Bug.pm @@ -431,6 +431,10 @@ A hash with one element, C. This is the id of the newly-filed bug. =over +=item 51 (Invalid Object) + +The component you specified is not valid for this Product. + =item 103 (Invalid Alias) The alias you specified is invalid for some reason. See the error message @@ -443,8 +447,7 @@ have more detail. =item 105 (Invalid Component) -Either you didn't specify a component, or the component you specified was -invalid. +You didn't specify a component. =item 106 (Invalid Product) diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm index 139ec1b7b..2dfb0b112 100755 --- a/Bugzilla/WebService/Constants.pm +++ b/Bugzilla/WebService/Constants.pm @@ -50,6 +50,9 @@ use base qw(Exporter); # comment that it was retired. Also, if an error changes its name, you'll # have to fix it here. 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. improper_bug_id_field_value => 100, bug_id_does_not_exist => 101, @@ -65,7 +68,6 @@ use constant WS_ERROR_CODE => { # Component errors require_component => 105, component_name_too_long => 105, - component_not_valid => 105, # Invalid Product no_products => 106, entry_access_denied => 106, diff --git a/editcomponents.cgi b/editcomponents.cgi index 8628f7762..dd693c05f 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -22,46 +22,19 @@ # Terry Weissman # Frédéric Buclin # Akamai Technologies -# -# Direct any questions on this source code to -# -# Holger Schurig use strict; use lib "."; use Bugzilla; use Bugzilla::Constants; -use Bugzilla::Series; use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::User; use Bugzilla::Component; -use Bugzilla::Bug; 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 $dbh = Bugzilla->dbh; my $template = Bugzilla->template; my $vars = {}; @@ -70,7 +43,6 @@ my $vars = {}; # my $user = Bugzilla->login(LOGIN_REQUIRED); -my $whoid = $user->id; print $cgi->header(); @@ -115,16 +87,13 @@ my $product = $user->check_can_admin_product($product_name); # unless ($action) { - $vars->{'showbugcounts'} = $showbugcounts; $vars->{'product'} = $product; $template->process("admin/components/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); - exit; } - # # action='add' -> present form for parameters for new component # @@ -136,12 +105,9 @@ if ($action eq 'add') { $vars->{'product'} = $product; $template->process("admin/components/create.html.tmpl", $vars) || ThrowTemplateError($template->error()); - exit; } - - # # action='new' -> add component entered in the 'action=add' screen # @@ -160,103 +126,23 @@ if ($action eq 'new') { my $description = trim($cgi->param('description') || ''); 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 = - new Bugzilla::Component({product => $product, - name => $comp_name}); - - if ($component) { - ThrowUserError('component_already_exists', - {'name' => $component->name}); - } - - $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(); - } + Bugzilla::Component->create({ name => $comp_name, + product => $product, + description => $description, + initialowner => $default_assignee, + initialqacontact => $default_qa_contact, + initial_cc => \@initial_cc }); $vars->{'comp'} = $component; $vars->{'product'} = $product; delete_token($token); - $template->process("admin/components/created.html.tmpl", - $vars) + $template->process("admin/components/created.html.tmpl", $vars) || ThrowTemplateError($template->error()); - exit; } - - # # action='del' -> ask if user really wants to delete # @@ -266,18 +152,14 @@ if ($action eq 'new') { if ($action eq 'del') { $vars->{'token'} = issue_session_token('delete_component'); $vars->{'comp'} = - Bugzilla::Component::check_component($product, $comp_name); - + Bugzilla::Component->check({ product => $product, name => $comp_name }); $vars->{'product'} = $product; $template->process("admin/components/confirm-delete.html.tmpl", $vars) || ThrowTemplateError($template->error()); - exit; } - - # # action='delete' -> really delete the component # @@ -285,34 +167,9 @@ if ($action eq 'del') { if ($action eq 'delete') { check_token_data($token, 'delete_component'); my $component = - Bugzilla::Component::check_component($product, $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); + Bugzilla::Component->check({ product => $product, name => $comp_name }); - $dbh->bz_commit_transaction(); + $component->remove_from_db; $vars->{'comp'} = $component; $vars->{'product'} = $product; @@ -323,8 +180,6 @@ if ($action eq 'delete') { exit; } - - # # action='edit' -> present the edit component form # @@ -334,7 +189,7 @@ if ($action eq 'delete') { if ($action eq 'edit') { $vars->{'token'} = issue_session_token('edit_component'); my $component = - Bugzilla::Component::check_component($product, $comp_name); + Bugzilla::Component->check({ product => $product, name => $comp_name }); $vars->{'comp'} = $component; $vars->{'initial_cc_names'} = @@ -342,15 +197,11 @@ if ($action eq 'edit') { $vars->{'product'} = $product; - $template->process("admin/components/edit.html.tmpl", - $vars) + $template->process("admin/components/edit.html.tmpl", $vars) || ThrowTemplateError($template->error()); - exit; } - - # # action='update' -> update the component # @@ -370,105 +221,23 @@ if ($action eq 'update') { my $description = trim($cgi->param('description') || ''); my @initial_cc = $cgi->param('initialcc'); - my $component_old = - Bugzilla::Component::check_component($product, $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; - } + my $component = + Bugzilla::Component->check({ product => $product, name => $comp_old_name }); - $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->{'initial_cc_names'} = - join(', ', map($_->login, @{$component->initial_cc})); $vars->{'product'} = $product; + $vars->{'changes'} = $changes; delete_token($token); - $template->process("admin/components/updated.html.tmpl", - $vars) + $template->process("admin/components/updated.html.tmpl", $vars) || ThrowTemplateError($template->error()); - exit; } diff --git a/editflagtypes.cgi b/editflagtypes.cgi index 02e043c5d..e32b7f509 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -600,7 +600,8 @@ sub validateComponent { ($product && $product->id) || 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; } diff --git a/request.cgi b/request.cgi index 886302d56..186b42e1c 100755 --- a/request.cgi +++ b/request.cgi @@ -196,8 +196,8 @@ sub queue { push(@criteria, "bugs.product_id = " . $product->id); push(@excluded_columns, 'product') unless $cgi->param('do_union'); if (defined $cgi->param('component') && $cgi->param('component') ne "") { - my $component = - Bugzilla::Component::check_component($product, scalar $cgi->param('component')); + my $component = Bugzilla::Component->check({ product => $product, + name => scalar $cgi->param('component') }); push(@criteria, "bugs.component_id = " . $component->id); push(@excluded_columns, 'component') unless $cgi->param('do_union'); } diff --git a/template/en/default/admin/components/create.html.tmpl b/template/en/default/admin/components/create.html.tmpl index 2cefd6d93..5e414d52a 100644 --- a/template/en/default/admin/components/create.html.tmpl +++ b/template/en/default/admin/components/create.html.tmpl @@ -95,8 +95,6 @@
- - diff --git a/template/en/default/admin/components/updated.html.tmpl b/template/en/default/admin/components/updated.html.tmpl index 176d653b7..1b94421b1 100644 --- a/template/en/default/admin/components/updated.html.tmpl +++ b/template/en/default/admin/components/updated.html.tmpl @@ -21,27 +21,12 @@ #%] [%# INTERFACE: - # - # '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 + # changes: hashref; contains changes made to the component. # # comp: object; Bugzilla::Component object representing the component # user updated. # product: object; Bugzilla::Product object representing the product to # 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 @@ -50,7 +35,11 @@ title = title %] -[% IF updated_description %] +[% IF changes.name.defined %] +

Updated Component name to: '[% comp.name FILTER html %]'.

+[% END %] + +[% IF changes.description.defined %] @@ -59,11 +48,11 @@
Updated description to:
[% END %] -[% IF updated_initialowner %] +[% IF changes.initialowner.defined %]

Updated Default Assignee to: '[% comp.default_assignee.login FILTER html %]'.

[% END %] -[% IF updated_initialqacontact %] +[% IF changes.initialqacontact.defined %]

[% IF comp.default_qa_contact.id %] Updated Default QA Contact to '[% comp.default_qa_contact.login FILTER html %]'. @@ -73,22 +62,19 @@

[% END %] -[% IF updated_name %] -

Updated Component name to: '[% comp.name FILTER html %]'.

-[% END %] - -[% IF updated_initialcc %] - [% IF initial_cc_names %] -

Updated Default CC list to: - '[% initial_cc_names FILTER html %]'.

+[% IF changes.cc_list.defined %] + [% IF comp.initial_cc.size %] + [% cc_list = [] %] + [% FOREACH cc_user = comp.initial_cc %] + [% cc_list.push(cc_user.login) %] + [% END %] +

Updated Default CC list to: [% cc_list.join(", ") FILTER html %].

[% ELSE %]

Removed the Default CC list.

[% END %] [% END %] -[% UNLESS updated_description || updated_initialowner || - updated_initialqacontact || updated_name || - updated_initialcc %] +[% UNLESS changes.keys.size %]

Nothing changed for component '[% comp.name FILTER html %]'.

[% END %] diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl index b9b423ca6..aa7068add 100644 --- a/template/en/default/global/messages.html.tmpl +++ b/template/en/default/global/messages.html.tmpl @@ -451,6 +451,12 @@ # we can still use get_text(). %] [% 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" %] [% title = "Sudo session started" %] The sudo session has been started. For the next 6 hours, or until you diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 4a5cd58d5..77ce40f72 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -298,12 +298,13 @@ [% ELSIF error == "component_already_exists" %] [% title = "Component Already Exists" %] - A component with the name '[% name FILTER html %]' already exists. + The [% product.name FILTER html %] product already has + a component named [% name FILTER html %]. [% ELSIF error == "component_blank_description" %] [% 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" %] [% title = "Blank Component Name Not Allowed" %] You must enter a name for this new component. @@ -317,16 +318,11 @@ [% ELSIF error == "component_name_too_long" %] [% title = "Component Name Is Too Long" %] 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" %] [% title = "Component Requires Default Assignee" %] - You must enter a default assignee for component '[% name FILTER html %]'. - - [% ELSIF error == "component_not_valid" %] - [% title = "Specified Component Does Not Exist" %] - Product [% product FILTER html %] does not have a component - named [% name FILTER html %]. + A default assignee is required for this component. [% ELSIF error == "customfield_nonexistent" %] [% title = "Unknown Custom Field" %] @@ -1620,6 +1616,8 @@ [% BLOCK object_name %] [% IF class == "Bugzilla::User" %] user + [% ELSIF class == "Bugzilla::Component" %] + component [% ELSIF class == "Bugzilla::Version" %] version [% ELSIF class == "Bugzilla::Milestone" %] -- cgit v1.2.3-24-g4f1b