From 424b21e37cd9aeac01588ce0defd3ee665944b1d Mon Sep 17 00:00:00 2001 From: Simon Green Date: Thu, 14 Aug 2014 09:40:47 +1000 Subject: Bug 1012506 - Allow a bug to have multiple aliases r=dkl, a=sgreen --- Bugzilla/Bug.pm | 157 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 122 insertions(+), 35 deletions(-) (limited to 'Bugzilla/Bug.pm') diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index c4ddb943c..07266da9c 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -49,7 +49,7 @@ use parent qw(Bugzilla::Object Exporter); use constant DB_TABLE => 'bugs'; use constant ID_FIELD => 'bug_id'; -use constant NAME_FIELD => 'alias'; +use constant NAME_FIELD => 'bug_id'; use constant LIST_ORDER => ID_FIELD; # Bugs have their own auditing table, bugs_activity. use constant AUDIT_CREATES => 0; @@ -65,7 +65,6 @@ sub DB_COLUMNS { my @custom_names = map {$_->name} @custom; my @columns = (qw( - alias assigned_to bug_file_loc bug_id @@ -208,7 +207,6 @@ sub UPDATE_COLUMNS { Bugzilla->active_custom_fields; my @custom_names = map {$_->name} @custom; my @columns = qw( - alias assigned_to bug_file_loc bug_severity @@ -318,7 +316,16 @@ sub new { || (ref($param) && $param->{id} =~ /\D/)) { if ($param) { - $param = { name => ref($param) ? $param->{id} : $param, + my $alias = ref($param) ? $param->{id} : $param; + my $bug_id = bug_alias_to_id($alias); + if (! $bug_id) { + my $error_self = {}; + bless $error_self, $class; + $error_self->{'bug_id'} = $alias; + $error_self->{'error'} = 'InvalidBugId'; + return $error_self; + } + $param = { id => $bug_id, cache => ref($param) ? $param->{cache} : 0 }; } else { @@ -690,6 +697,7 @@ sub create { # These are not a fields in the bugs table, so we don't pass them to # insert_create_data. + my $bug_aliases = delete $params->{alias}; my $cc_ids = delete $params->{cc}; my $groups = delete $params->{groups}; my $depends_on = delete $params->{dependson}; @@ -788,6 +796,13 @@ sub create { # but sometimes it's blank. Bugzilla::Comment->insert_create_data($creation_comment); + # Set up aliases + my $sth_aliases = $dbh->prepare('INSERT INTO bugs_aliases (alias, bug_id) VALUES (?, ?)'); + foreach my $alias (@$bug_aliases) { + trick_taint($alias); + $sth_aliases->execute($alias, $bug->bug_id); + } + Bugzilla::Hook::process('bug_end_of_create', { bug => $bug, timestamp => $timestamp, }); @@ -907,7 +922,26 @@ sub update { my $added_names = join(', ', (map {$_->login} @$added_users)); $changes->{cc} = [$removed_names, $added_names]; } - + + # Aliases + my $old_aliases = $old_bug->alias; + my $new_aliases = $self->alias; + my ($removed_aliases, $added_aliases) = diff_arrays($old_aliases, $new_aliases); + + foreach my $alias (@$removed_aliases) { + $dbh->do('DELETE FROM bugs_aliases WHERE bug_id = ? AND alias = ?', + undef, $self->id, $alias); + } + foreach my $alias (@$added_aliases) { + trick_taint($alias); + $dbh->do('INSERT INTO bugs_aliases (bug_id, alias) VALUES (?,?)', + undef, $self->id, $alias); + } + # If any changes were found, record it in the activity log + if (scalar @$removed_aliases || scalar @$added_aliases) { + $changes->{alias} = [join(', ', @$removed_aliases), join(', ', @$added_aliases)]; + } + # Keywords my @old_kw_ids = map { $_->id } @{$old_bug->keyword_objects}; my @new_kw_ids = map { $_->id } @{$self->keyword_objects}; @@ -1311,32 +1345,38 @@ sub _send_bugmail { ##################################################################### sub _check_alias { - my ($invocant, $alias) = @_; - $alias = trim($alias); - return undef if (!$alias); - - # Make sure the alias isn't too long. - if (length($alias) > 20) { - ThrowUserError("alias_too_long"); - } - # Make sure the alias isn't just a number. - if ($alias =~ /^\d+$/) { - ThrowUserError("alias_is_numeric", { alias => $alias }); - } - # Make sure the alias has no commas or spaces. - if ($alias =~ /[, ]/) { - ThrowUserError("alias_has_comma_or_space", { alias => $alias }); - } - # Make sure the alias is unique, or that it's already our alias. - my $other_bug = new Bugzilla::Bug($alias); - if (!$other_bug->{error} - && (!ref $invocant || $other_bug->id != $invocant->id)) - { - ThrowUserError("alias_in_use", { alias => $alias, - bug_id => $other_bug->id }); + my ($invocant, $aliases) = @_; + $aliases = ref $aliases ? $aliases : [split(/[\s,]+/, $aliases)]; + + # Remove empty aliases + @$aliases = grep { $_ } @$aliases; + + foreach my $alias (@$aliases) { + $alias = trim($alias); + + # Make sure the alias isn't too long. + if (length($alias) > 40) { + ThrowUserError("alias_too_long"); + } + # Make sure the alias isn't just a number. + if ($alias =~ /^\d+$/) { + ThrowUserError("alias_is_numeric", { alias => $alias }); + } + # Make sure the alias has no commas or spaces. + if ($alias =~ /[, ]/) { + ThrowUserError("alias_has_comma_or_space", { alias => $alias }); + } + # Make sure the alias is unique, or that it's already our alias. + my $other_bug = new Bugzilla::Bug($alias); + if (!$other_bug->{error} + && (!ref $invocant || $other_bug->id != $invocant->id)) + { + ThrowUserError("alias_in_use", { alias => $alias, + bug_id => $other_bug->id }); + } } - return $alias; + return $aliases; } sub _check_assigned_to { @@ -2376,6 +2416,13 @@ sub set_all { work_time => $params->{'work_time'} }); } + if (exists $params->{alias} && $params->{alias}{set}) { + $params->{alias} = { + add => $params->{alias}{set}, + remove => $self->alias, + }; + } + my %normal_set_all; foreach my $name (keys %$params) { # These are handled separately below. @@ -2400,6 +2447,7 @@ sub set_all { } $self->_add_remove($params, 'cc'); + $self->_add_remove($params, 'alias'); # Theoretically you could move a product without ever specifying # a new assignee or qa_contact, or adding/removing any CCs. So, @@ -2416,14 +2464,13 @@ sub _add_remove { my ($self, $params, $name) = @_; my @add = @{ $params->{$name}->{add} || [] }; my @remove = @{ $params->{$name}->{remove} || [] }; - $name =~ s/s$//; + $name =~ s/s$// if $name ne 'alias'; my $add_method = "add_$name"; my $remove_method = "remove_$name"; $self->$add_method($_) foreach @add; $self->$remove_method($_) foreach @remove; } -sub set_alias { $_[0]->set('alias', $_[1]); } sub set_assigned_to { my ($self, $value) = @_; $self->set('assigned_to', $value); @@ -2840,6 +2887,21 @@ sub remove_cc { @$cc_users = grep { $_->id != $user->id } @$cc_users; } +sub add_alias { + my ($self, $alias) = @_; + return if !$alias; + my $aliases = $self->_check_alias($alias); + $alias = $aliases->[0]; + my $bug_aliases = $self->alias; + push(@$bug_aliases, $alias) if !grep($_ eq $alias, @$bug_aliases); +} + +sub remove_alias { + my ($self, $alias) = @_; + my $bug_aliases = $self->alias; + @$bug_aliases = grep { $_ ne $alias } @$bug_aliases; +} + # $bug->add_comment("comment", {isprivate => 1, work_time => 10.5, # type => CMT_NORMAL, extra_data => $data}); sub add_comment { @@ -3167,7 +3229,6 @@ sub tags { # These are accessors that don't need to access the database. # Keep them in alphabetical order. -sub alias { return $_[0]->{alias} } sub bug_file_loc { return $_[0]->{bug_file_loc} } sub bug_id { return $_[0]->{bug_id} } sub bug_severity { return $_[0]->{bug_severity} } @@ -3277,6 +3338,24 @@ sub actual_time { return $self->{'actual_time'}; } +sub alias { + my ($self) = @_; + return $self->{'alias'} if exists $self->{'alias'}; + return [] if $self->{'error'}; + + my $dbh = Bugzilla->dbh; + $self->{'alias'} = $dbh->selectcol_arrayref( + q{SELECT alias + FROM bugs_aliases + WHERE bug_id = ? + ORDER BY alias}, + undef, $self->bug_id); + + $self->{'alias'} = [] if !scalar(@{$self->{'alias'}}); + + return $self->{'alias'}; +} + sub any_flags_requesteeble { my ($self) = @_; return $self->{'any_flags_requesteeble'} @@ -3868,7 +3947,7 @@ sub bug_alias_to_id { my $dbh = Bugzilla->dbh; trick_taint($alias); return $dbh->selectrow_array( - "SELECT bug_id FROM bugs WHERE alias = ?", undef, $alias); + "SELECT bug_id FROM bugs_aliases WHERE alias = ?", undef, $alias); } ##################################################################### @@ -4497,6 +4576,16 @@ __END__ Ensures the accessors for custom fields are always created. +=item C + +Adds an alias to the internal respresentation of the bug. You will need to +call L to make the changes permanent. + +=item C + +Removes an alias from the internal respresentation of the bug. You will need to +call L to make the changes permanent. + =item C Creates or updates a L for this bug and the supplied @@ -4664,8 +4753,6 @@ $user, the timestamp given as $last_visit. =item remove_group -=item set_alias - =item set_dup_id =item set_target_milestone -- cgit v1.2.3-24-g4f1b