# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # This Source Code Form is "Incompatible With Secondary Licenses", as # defined by the Mozilla Public License, v. 2.0. package Bugzilla::Extension::OldBugMove; use strict; use base qw(Bugzilla::Extension); use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Field::Choice; use Bugzilla::Mailer; use Bugzilla::User; use Bugzilla::Util qw(trim); use Scalar::Util qw(blessed); use Storable qw(dclone); use constant VERSION => BUGZILLA_VERSION; # This is 4 because that's what it originally was when this code was # a part of Bugzilla. use constant CMT_MOVED_TO => 4; sub install_update_db { my $reso_type = Bugzilla::Field::Choice->type('resolution'); my $moved_reso = $reso_type->new({ name => 'MOVED' }); # We make the MOVED resolution inactive, so that it doesn't show up # as a valid drop-down option. if ($moved_reso) { $moved_reso->set_is_active(0); $moved_reso->update(); } else { print "Creating the MOVED resolution...\n"; $reso_type->create( { value => 'MOVED', sortkey => '30000', isactive => 0 }); } } sub config_add_panels { my ($self, $args) = @_; my $modules = $args->{'panel_modules'}; $modules->{'OldBugMove'} = 'Bugzilla::Extension::OldBugMove::Params'; } sub template_before_create { my ($self, $args) = @_; my $config = $args->{config}; my $constants = $config->{CONSTANTS}; $constants->{CMT_MOVED_TO} = CMT_MOVED_TO; my $vars = $config->{VARIABLES}; $vars->{oldbugmove_user_is_mover} = \&_user_is_mover; } sub object_before_delete { my ($self, $args) = @_; my $object = $args->{'object'}; if ($object->isa('Bugzilla::Field::Choice::resolution')) { if ($object->name eq 'MOVED') { ThrowUserError('oldbugmove_no_delete_moved'); } } } sub object_before_set { my ($self, $args) = @_; my ($object, $field) = @$args{qw(object field)}; if ($field eq 'resolution' and $object->isa('Bugzilla::Bug')) { # Store the old value so that end_of_set can check it. $object->{'_oldbugmove_old_resolution'} = $object->resolution; } } sub object_end_of_set { my ($self, $args) = @_; my ($object, $field) = @$args{qw(object field)}; if ($field eq 'resolution' and $object->isa('Bugzilla::Bug')) { my $old_value = delete $object->{'_oldbugmove_old_resolution'}; return if $old_value eq $object->resolution; if ($object->resolution eq 'MOVED') { $object->add_comment('', { type => CMT_MOVED_TO, extra_data => Bugzilla->user->login }); } } } sub object_end_of_set_all { my ($self, $args) = @_; my $object = $args->{'object'}; if ($object->isa('Bugzilla::Bug') and Bugzilla->input_params->{'oldbugmove'}) { my $new_status = Bugzilla->params->{'duplicate_or_move_bug_status'}; $object->set_bug_status($new_status, { resolution => 'MOVED' }); } } sub object_validators { my ($self, $args) = @_; my ($class, $validators) = @$args{qw(class validators)}; if ($class->isa('Bugzilla::Comment')) { my $extra_data_validator = $validators->{extra_data}; $validators->{extra_data} = sub { _check_comment_extra_data($extra_data_validator, @_) }; } elsif ($class->isa('Bugzilla::Bug')) { my $reso_validator = $validators->{resolution}; $validators->{resolution} = sub { _check_bug_resolution($reso_validator, @_) }; } } sub _check_bug_resolution { my $original_validator = shift; my ($invocant, $resolution) = @_; if ($resolution eq 'MOVED' and !Bugzilla->input_params->{'oldbugmove'}) { # MOVED has a special meaning and can only be used when # really moving bugs to another installation. ThrowUserError('oldbugmove_no_manual_move'); } return $original_validator->(@_); } sub _check_comment_extra_data { my $original_validator = shift; my ($invocant, $extra_data, undef, $params) = @_; my $type = blessed($invocant) ? $invocant->type : $params->{type}; if ($type == CMT_MOVED_TO) { return Bugzilla::User->check($extra_data)->login; } return $original_validator->(@_); } sub bug_end_of_update { my ($self, $args) = @_; my ($bug, $old_bug, $changes) = @$args{qw(bug old_bug changes)}; if (defined $changes->{'resolution'} and $changes->{'resolution'}->[1] eq 'MOVED') { $self->_move_bug($bug, $old_bug); } } sub _move_bug { my ($self, $bug, $old_bug) = @_; my $dbh = Bugzilla->dbh; my $template = Bugzilla->template; _user_is_mover(Bugzilla->user) or ThrowUserError("auth_failure", { action => 'move', object => 'bugs' }); # Don't export the new status and resolution. We want the current # ones. local $Storable::forgive_me = 1; my $export_me = dclone($bug); $export_me->{bug_status} = $old_bug->bug_status; delete $export_me->{status}; $export_me->{resolution} = $old_bug->resolution; # Prepare and send all data about these bugs to the new database my $to = Bugzilla->params->{'move-to-address'}; $to =~ s/@/\@/; my $from = Bugzilla->params->{'mailfrom'}; $from =~ s/@/\@/; my $msg = "To: $to\n"; $msg .= "From: Bugzilla <" . $from . ">\n"; $msg .= "Subject: Moving bug " . $bug->id . "\n\n"; my @fieldlist = (Bugzilla::Bug->fields, 'group', 'long_desc', 'attachment', 'attachmentdata'); my %displayfields = map { $_ => 1 } @fieldlist; my $vars = { bugs => [$export_me], displayfields => \%displayfields }; $template->process("bug/show.xml.tmpl", $vars, \$msg) || ThrowTemplateError($template->error()); $msg .= "\n"; MessageToMTA($msg); } sub _user_is_mover { my $user = shift; my @movers = map { trim($_) } split(',', Bugzilla->params->{'movers'}); return ($user->id and grep($_ eq $user->login, @movers)) ? 1 : 0; } __PACKAGE__->NAME;