diff options
Diffstat (limited to 'Bugzilla/Flag.pm')
-rw-r--r-- | Bugzilla/Flag.pm | 124 |
1 files changed, 89 insertions, 35 deletions
diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index a727532a6..089ac6c9f 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -81,15 +81,21 @@ use constant AUDIT_REMOVES => 0; use constant SKIP_REQUESTEE_ON_ERROR => 1; -use constant DB_COLUMNS => qw( - id - type_id - bug_id - attach_id - requestee_id - setter_id - status -); +sub DB_COLUMNS { + my $dbh = Bugzilla->dbh; + return qw( + id + type_id + bug_id + attach_id + requestee_id + setter_id + status), + $dbh->sql_date_format('creation_date', '%Y.%m.%d %H:%i:%s') . + ' AS creation_date', + $dbh->sql_date_format('modification_date', '%Y.%m.%d %H:%i:%s') . + ' AS modification_date'; +} use constant UPDATE_COLUMNS => qw( requestee_id @@ -134,6 +140,14 @@ Returns the ID of the attachment this flag belongs to, if any. Returns the status '+', '-', '?' of the flag. +=item C<creation_date> + +Returns the timestamp when the flag was created. + +=item C<modification_date> + +Returns the timestamp when the flag was last modified. + =back =cut @@ -146,6 +160,8 @@ sub attach_id { return $_[0]->{'attach_id'}; } sub status { return $_[0]->{'status'}; } sub setter_id { return $_[0]->{'setter_id'}; } sub requestee_id { return $_[0]->{'requestee_id'}; } +sub creation_date { return $_[0]->{'creation_date'}; } +sub modification_date { return $_[0]->{'modification_date'}; } ############################### #### Methods #### @@ -180,22 +196,23 @@ is an attachment flag, else undefined. sub type { my $self = shift; - $self->{'type'} ||= new Bugzilla::FlagType($self->{'type_id'}); - return $self->{'type'}; + return $self->{'type'} + ||= new Bugzilla::FlagType($self->{'type_id'}, cache => 1 ); } sub setter { my $self = shift; - $self->{'setter'} ||= new Bugzilla::User($self->{'setter_id'}); - return $self->{'setter'}; + return $self->{'setter' } + ||= new Bugzilla::User({ id => $self->{'setter_id'}, cache => 1 }); } sub requestee { my $self = shift; if (!defined $self->{'requestee'} && $self->{'requestee_id'}) { - $self->{'requestee'} = new Bugzilla::User($self->{'requestee_id'}); + $self->{'requestee'} + = new Bugzilla::User({ id => $self->{'requestee_id'}, cache => 1 }); } return $self->{'requestee'}; } @@ -205,16 +222,16 @@ sub attachment { return undef unless $self->attach_id; require Bugzilla::Attachment; - $self->{'attachment'} ||= new Bugzilla::Attachment($self->attach_id); - return $self->{'attachment'}; + return $self->{'attachment'} + ||= new Bugzilla::Attachment({ id => $self->attach_id, cache => 1 }); } sub bug { my $self = shift; require Bugzilla::Bug; - $self->{'bug'} ||= new Bugzilla::Bug($self->bug_id); - return $self->{'bug'}; + return $self->{'bug'} + ||= new Bugzilla::Bug({ id => $self->bug_id, cache => 1 }); } ################################ @@ -284,7 +301,7 @@ sub count { sub set_flag { my ($class, $obj, $params) = @_; - my ($bug, $attachment); + my ($bug, $attachment, $obj_flag, $requestee_changed); if (blessed($obj) && $obj->isa('Bugzilla::Attachment')) { $attachment = $obj; $bug = $attachment->bug; @@ -296,6 +313,12 @@ sub set_flag { ThrowCodeError('flag_unexpected_object', { 'caller' => ref $obj }); } + # Make sure the user can change flags + my $privs; + $bug->check_can_change_field('flagtypes.name', 0, 1, \$privs) + || ThrowUserError('illegal_change', + { field => 'flagtypes.name', privs => $privs }); + # Update (or delete) an existing flag. if ($params->{id}) { my $flag = $class->check({ id => $params->{id} }); @@ -322,13 +345,14 @@ sub set_flag { ($obj_flagtype) = grep { $_->id == $flag->type_id } @{$obj->flag_types}; push(@{$obj_flagtype->{flags}}, $flag); } - my ($obj_flag) = grep { $_->id == $flag->id } @{$obj_flagtype->{flags}}; + ($obj_flag) = grep { $_->id == $flag->id } @{$obj_flagtype->{flags}}; # If the flag has the correct type but cannot be found above, this means # the flag is going to be removed (e.g. because this is a pending request # and the attachment is being marked as obsolete). return unless $obj_flag; - $class->_validate($obj_flag, $obj_flagtype, $params, $bug, $attachment); + ($obj_flag, $requestee_changed) = + $class->_validate($obj_flag, $obj_flagtype, $params, $bug, $attachment); } # Create a new flag. elsif ($params->{type_id}) { @@ -360,12 +384,21 @@ sub set_flag { } } - $class->_validate(undef, $obj_flagtype, $params, $bug, $attachment); + ($obj_flag, $requestee_changed) = + $class->_validate(undef, $obj_flagtype, $params, $bug, $attachment); } else { ThrowCodeError('param_required', { function => $class . '->set_flag', param => 'id/type_id' }); } + + if ($obj_flag + && $requestee_changed + && $obj_flag->requestee_id + && $obj_flag->requestee->setting('requestee_cc') eq 'on') + { + $bug->add_cc($obj_flag->requestee); + } } sub _validate { @@ -383,25 +416,27 @@ sub _validate { my $old_requestee_id = $obj_flag->requestee_id; $obj_flag->_set_status($params->{status}); - $obj_flag->_set_requestee($params->{requestee}, $attachment, $params->{skip_roe}); + $obj_flag->_set_requestee($params->{requestee}, $bug, $attachment, $params->{skip_roe}); + + # The requestee ID can be undefined. + my $requestee_changed = ($obj_flag->requestee_id || 0) != ($old_requestee_id || 0); # The setter field MUST NOT be updated if neither the status # nor the requestee fields changed. - if (($obj_flag->status ne $old_status) - # The requestee ID can be undefined. - || (($obj_flag->requestee_id || 0) != ($old_requestee_id || 0))) - { + if (($obj_flag->status ne $old_status) || $requestee_changed) { $obj_flag->_set_setter($params->{setter}); } # If the flag is deleted, remove it from the list. if ($obj_flag->status eq 'X') { @{$flag_type->{flags}} = grep { $_->id != $obj_flag->id } @{$flag_type->{flags}}; + return; } # Add the newly created flag to the list. elsif (!$obj_flag->id) { push(@{$flag_type->{flags}}, $obj_flag); } + return wantarray ? ($obj_flag, $requestee_changed) : $obj_flag; } =pod @@ -418,10 +453,14 @@ Creates a flag record in the database. sub create { my ($class, $flag, $timestamp) = @_; - $timestamp ||= Bugzilla->dbh->selectrow_array('SELECT NOW()'); + $timestamp ||= Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); my $params = {}; my @columns = grep { $_ ne 'id' } $class->_get_db_columns; + + # Some columns use date formatting so use alias instead + @columns = map { /\s+AS\s+(.*)$/ ? $1 : $_ } @columns; + $params->{$_} = $flag->{$_} foreach @columns; $params->{creation_date} = $params->{modification_date} = $timestamp; @@ -440,6 +479,7 @@ sub update { if (scalar(keys %$changes)) { $dbh->do('UPDATE flags SET modification_date = ? WHERE id = ?', undef, ($timestamp, $self->id)); + $self->{'modification_date'} = format_time($timestamp, '%Y.%m.%d %T'); } return $changes; } @@ -602,10 +642,10 @@ sub force_retarget { ############################### sub _set_requestee { - my ($self, $requestee, $attachment, $skip_requestee_on_error) = @_; + my ($self, $requestee, $bug, $attachment, $skip_requestee_on_error) = @_; $self->{requestee} = - $self->_check_requestee($requestee, $attachment, $skip_requestee_on_error); + $self->_check_requestee($requestee, $bug, $attachment, $skip_requestee_on_error); $self->{requestee_id} = $self->{requestee} ? $self->{requestee}->id : undef; @@ -627,7 +667,7 @@ sub _set_status { } sub _check_requestee { - my ($self, $requestee, $attachment, $skip_requestee_on_error) = @_; + my ($self, $requestee, $bug, $attachment, $skip_requestee_on_error) = @_; # If the flag status is not "?", then no requestee can be defined. return undef if ($self->status ne '?'); @@ -647,15 +687,29 @@ sub _check_requestee { # is specifically requestable. For existing flags, if the requestee # was set before the flag became specifically unrequestable, the # user can either remove him or leave him alone. - ThrowCodeError('flag_requestee_disabled', { type => $self->type }) + ThrowCodeError('flag_type_requestee_disabled', { type => $self->type }) if !$self->type->is_requesteeble; + # BMO customisation: + # You can't ask a disabled account, as they don't have the ability to + # set the flag. + ThrowUserError('flag_requestee_disabled', { requestee => $requestee }) + if !$requestee->is_enabled; + # Make sure the requestee can see the bug. # Note that can_see_bug() will query the DB, so if the bug # is being added/removed from some groups and these changes # haven't been committed to the DB yet, they won't be taken - # into account here. In this case, old restrictions matters. - if (!$requestee->can_see_bug($self->bug_id)) { + # into account here. In this case, old group restrictions matter. + # However, if the user has just been changed to the assignee, + # qa_contact, or added to the cc list of the bug and the bug + # is cclist_accessible, the requestee is allowed. + if (!$requestee->can_see_bug($self->bug_id) + && (!$bug->cclist_accessible + || !grep($_->id == $requestee->id, @{ $bug->cc_users }) + && $requestee->id != $bug->assigned_to->id + && (!$bug->qa_contact || $requestee->id != $bug->qa_contact->id))) + { if ($skip_requestee_on_error) { undef $requestee; } @@ -955,7 +1009,7 @@ sub notify { my %recipients; foreach my $cc (split(/[, ]+/, $cc_list)) { - my $ccuser = new Bugzilla::User({ name => $cc }); + my $ccuser = new Bugzilla::User({ name => $cc, cache => 1 }); next if (scalar(@bug_in_groups) && (!$ccuser || !$ccuser->can_see_bug($bug->bug_id))); next if $attachment_is_private && (!$ccuser || !$ccuser->is_insider); # Prevent duplicated entries due to case sensitivity. |