summaryrefslogtreecommitdiffstats
path: root/Bugzilla/Bug.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla/Bug.pm')
-rw-r--r--Bugzilla/Bug.pm138
1 files changed, 117 insertions, 21 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm
index cad024bbd..3d5ebf553 100644
--- a/Bugzilla/Bug.pm
+++ b/Bugzilla/Bug.pm
@@ -333,10 +333,14 @@ sub new {
# If we get something that looks like a word (not a number),
# make it the "name" param.
- if (!defined $param || (!ref($param) && $param !~ /^\d+$/)) {
+ if (!defined $param
+ || (!ref($param) && (!$param || $param =~ /\D/))
+ || (ref($param) && (!$param->{id} || $param->{id} =~ /\D/)))
+ {
# But only if aliases are enabled.
if (Bugzilla->params->{'usebugaliases'} && $param) {
- $param = { name => $param };
+ $param = { name => ref($param) ? $param->{id} : $param,
+ cache => ref($param) ? $param->{cache} : 0 };
}
else {
# Aliases are off, and we got something that's not a number.
@@ -370,20 +374,31 @@ sub new {
return $self;
}
-sub check {
+sub cache_key {
my $class = shift;
- my ($id, $field) = @_;
+ my $key = $class->SUPER::cache_key(@_)
+ || return;
+ return $key . ',' . Bugzilla->user->id;
+}
- ThrowUserError('improper_bug_id_field_value', { field => $field }) unless defined $id;
+sub check {
+ my $class = shift;
+ my ($param, $field) = @_;
# Bugzilla::Bug throws lots of special errors, so we don't call
# SUPER::check, we just call our new and do our own checks.
- my $self = $class->new(trim($id));
- # For error messages, use the id that was returned by new(), because
- # it's cleaned up.
- $id = $self->id;
+ my $id = ref($param)
+ ? ($param->{id} = trim($param->{id}))
+ : ($param = trim($param));
+ ThrowUserError('improper_bug_id_field_value', { field => $field }) unless defined $id;
+
+ my $self = $class->new($param);
if ($self->{error}) {
+ # For error messages, use the id that was returned by new(), because
+ # it's cleaned up.
+ $id = $self->id;
+
if ($self->{error} eq 'NotFound') {
ThrowUserError("bug_id_does_not_exist", { bug_id => $id });
}
@@ -781,6 +796,10 @@ sub update {
my ($changes, $old_bug) = $self->SUPER::update(@_);
+ Bugzilla::Hook::process('bug_start_of_update',
+ { timestamp => $delta_ts, bug => $self,
+ old_bug => $old_bug, changes => $changes });
+
# Certain items in $changes have to be fixed so that they hold
# a name instead of an ID.
foreach my $field (qw(product_id component_id)) {
@@ -1633,6 +1652,14 @@ sub _check_groups {
: $params->{product};
my %add_groups;
+ # BMO: Allow extension to add groups before the
+ # real checks are done.
+ Bugzilla::Hook::process('bug_check_groups', {
+ product => $product,
+ group_names => $group_names,
+ add_groups => \%add_groups
+ });
+
# In email or WebServices, when the "groups" item actually
# isn't specified, then just add the default groups.
if (!defined $group_names) {
@@ -1651,7 +1678,12 @@ sub _check_groups {
foreach my $name (@$group_names) {
my $group = Bugzilla::Group->check_no_disclose({ %args, name => $name });
- if (!$product->group_is_settable($group)) {
+ # BMO: Do not check group_is_settable if the group is
+ # already added, such as from the extension hook. group_is_settable
+ # will reject any group the user is not currently in.
+ if (!$add_groups{$group->id}
+ && !$product->group_is_settable($group))
+ {
ThrowUserError('group_restriction_not_allowed', { %args, name => $name });
}
$add_groups{$group->id} = $group;
@@ -1660,7 +1692,7 @@ sub _check_groups {
# Now enforce mandatory groups.
$add_groups{$_->id} = $_ foreach @{ $product->groups_mandatory };
-
+
my @add_groups = values %add_groups;
return \@add_groups;
}
@@ -3235,7 +3267,8 @@ sub component_obj {
my ($self) = @_;
return $self->{component_obj} if defined $self->{component_obj};
return {} if $self->{error};
- $self->{component_obj} = new Bugzilla::Component($self->{component_id});
+ $self->{component_obj} =
+ new Bugzilla::Component({ id => $self->{component_id}, cache => 1 });
return $self->{component_obj};
}
@@ -3274,6 +3307,26 @@ sub depends_on_obj {
return $self->{depends_on_obj};
}
+sub duplicates {
+ my $self = shift;
+ return $self->{duplicates} if exists $self->{duplicates};
+ return [] if $self->{error};
+ $self->{duplicates} = Bugzilla::Bug->new_from_list($self->duplicate_ids);
+ return $self->{duplicates};
+}
+
+sub duplicate_ids {
+ my $self = shift;
+ return $self->{duplicate_ids} if exists $self->{duplicate_ids};
+ return [] if $self->{error};
+
+ my $dbh = Bugzilla->dbh;
+ $self->{duplicate_ids} =
+ $dbh->selectcol_arrayref('SELECT dupe FROM duplicates WHERE dupe_of = ?',
+ undef, $self->id);
+ return $self->{duplicate_ids};
+}
+
sub flag_types {
my ($self) = @_;
return $self->{'flag_types'} if exists $self->{'flag_types'};
@@ -3282,7 +3335,8 @@ sub flag_types {
my $vars = { target_type => 'bug',
product_id => $self->{product_id},
component_id => $self->{component_id},
- bug_id => $self->bug_id };
+ bug_id => $self->bug_id,
+ active_or_has_flags => $self->bug_id };
$self->{'flag_types'} = Bugzilla::Flag->_flag_types($vars);
return $self->{'flag_types'};
@@ -3383,7 +3437,8 @@ sub product {
sub product_obj {
my $self = shift;
return {} if $self->{error};
- $self->{product_obj} ||= new Bugzilla::Product($self->{product_id});
+ $self->{product_obj} ||=
+ new Bugzilla::Product({ id => $self->{product_id}, cache => 1 });
return $self->{product_obj};
}
@@ -3801,14 +3856,26 @@ sub GetBugActivity {
$changes = [];
}
+ # If this is the same field as the previoius item, then concatenate
+ # the data into the same change.
+ if ($operation->{'who'} && $who eq $operation->{'who'}
+ && $when eq $operation->{'when'}
+ && $fieldname eq $operation->{'fieldname'}
+ && ($attachid || 0) == ($operation->{'attachid'} || 0))
+ {
+ my $old_change = pop @$changes;
+ $removed = _join_activity_entries($fieldname, $old_change->{'removed'}, $removed);
+ $added = _join_activity_entries($fieldname, $old_change->{'added'}, $added);
+ }
+
$operation->{'who'} = $who;
$operation->{'when'} = $when;
+ $operation->{'fieldname'} = $change{'fieldname'} = $fieldname;
+ $operation->{'attachid'} = $change{'attachid'} = $attachid;
- $change{'fieldname'} = $fieldname;
- $change{'attachid'} = $attachid;
$change{'removed'} = $removed;
$change{'added'} = $added;
-
+
if ($comment_id) {
$change{'comment'} = Bugzilla::Comment->new($comment_id);
}
@@ -3825,6 +3892,37 @@ sub GetBugActivity {
return(\@operations, $incomplete_data);
}
+sub _join_activity_entries {
+ my ($field, $current_change, $new_change) = @_;
+ # We need to insert characters as these were removed by old
+ # LogActivityEntry code.
+
+ if ($current_change eq '') {
+ return $new_change;
+ }
+
+ # Buglists and see_also need the comma restored
+ if ($field eq 'dependson' || $field eq 'blocked' || $field eq 'see_also') {
+ if (substr($new_change, 0, 1) eq ',') {
+ return $current_change . $new_change;
+ } else {
+ return $current_change . ', ' . $new_change;
+ }
+ }
+
+ # Assume bug_file_loc contain a single url, don't insert a delimiter
+ if ($field eq 'bug_file_loc') {
+ return $current_change . $new_change;
+ }
+
+ # All other fields get a space
+ if (substr($new_change, 0, 1) eq ' ') {
+ return $current_change . $new_change;
+ } else {
+ return $current_change . ' ' . $new_change;
+ }
+}
+
# Update the bugs_activity table to reflect changes made in bugs.
sub LogActivityEntry {
my ($i, $col, $removed, $added, $whoid, $timestamp, $comment_id) = @_;
@@ -3839,7 +3937,6 @@ sub LogActivityEntry {
my $commaposition = find_wrap_point($removed, MAX_LINE_LENGTH);
$removestr = substr($removed, 0, $commaposition);
$removed = substr($removed, $commaposition);
- $removed =~ s/^[,\s]+//; # remove any comma or space
} else {
$removed = ""; # no more entries
}
@@ -3847,7 +3944,6 @@ sub LogActivityEntry {
my $commaposition = find_wrap_point($added, MAX_LINE_LENGTH);
$addstr = substr($added, 0, $commaposition);
$added = substr($added, $commaposition);
- $added =~ s/^[,\s]+//; # remove any comma or space
} else {
$added = ""; # no more entries
}
@@ -3934,8 +4030,8 @@ sub check_can_change_field {
return 1;
}
- # Allow anyone to change comments.
- if ($field =~ /^longdesc/) {
+ # Allow anyone to change comments, or set flags
+ if ($field =~ /^longdesc/ || $field eq 'flagtypes.name') {
return 1;
}