diff --git a/extensions/MozChangeField/Extension.pm b/extensions/MozChangeField/Extension.pm index 6e2dbffdae..4bb68085d4 100644 --- a/extensions/MozChangeField/Extension.pm +++ b/extensions/MozChangeField/Extension.pm @@ -17,6 +17,7 @@ use Bugzilla::Constants; use Bugzilla::Logging; use Bugzilla::Extension::MozChangeField::Pre::CanConfirm; +use Bugzilla::Extension::MozChangeField::Pre::CheckinNeededTB; use Bugzilla::Extension::MozChangeField::Pre::CommentClosedBugs; use Bugzilla::Extension::MozChangeField::Pre::CustomField; use Bugzilla::Extension::MozChangeField::Pre::Graveyard; @@ -25,6 +26,7 @@ use Bugzilla::Extension::MozChangeField::Pre::TypePriSevEditbugs; my @pre_instances = ( Bugzilla::Extension::MozChangeField::Pre::CanConfirm->new, + Bugzilla::Extension::MozChangeField::Pre::CheckinNeededTB->new, Bugzilla::Extension::MozChangeField::Pre::CommentClosedBugs->new, Bugzilla::Extension::MozChangeField::Pre::CustomField->new, Bugzilla::Extension::MozChangeField::Pre::Graveyard->new, @@ -50,6 +52,15 @@ my @post_instances = ( our $VERSION = '0.1'; +sub bug_start_of_set_all { + my ($self, $args) = @_; + + foreach my $instance (@pre_instances) { + next if !$instance->can('evaluate_set_all'); + $instance->evaluate_set_all($args); + } +} + sub bug_check_can_change_field { my ($self, $args) = @_; diff --git a/extensions/MozChangeField/lib/Pre/CheckinNeededTB.pm b/extensions/MozChangeField/lib/Pre/CheckinNeededTB.pm new file mode 100644 index 0000000000..bf4da6c250 --- /dev/null +++ b/extensions/MozChangeField/lib/Pre/CheckinNeededTB.pm @@ -0,0 +1,65 @@ +# 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::MozChangeField::Pre::CheckinNeededTB; + +use 5.10.1; + +use Moo; + +use Bugzilla::Error; + +use constant CHECKIN_KEYWORD => 'checkin-needed-tb'; +use constant PRODUCTS => ('Calendar', 'MailNews Core', 'Thunderbird'); + +sub evaluate_set_all { + my ($self, $args) = @_; + + my $bug = $args->{bug}; + my $params = $args->{params}; + + my $product + = exists $params->{product} ? $params->{product} : $bug->product_obj->name; + return unless grep { $_ eq $product } PRODUCTS; + + my $keyword_params = $params->{keywords}; + return unless $keyword_params; + + # This rule only applies when checkin-needed-tb is newly added. + return if $bug->has_keyword(CHECKIN_KEYWORD); + + my $keyword_added; + if (exists $keyword_params->{set}) { + $keyword_added = _contains_checkin_keyword($keyword_params->{set}); + } + else { + $keyword_added + = _contains_checkin_keyword($keyword_params->{add}) + && !_contains_checkin_keyword($keyword_params->{remove}); + } + return unless $keyword_added; + + my $milestone = exists $params->{target_milestone} + ? $params->{target_milestone} + : $bug->target_milestone; + + if (!defined $milestone || $milestone eq '' || $milestone eq '---') { + ThrowUserError('mozchangefield_checkin_needed_tb_requires_milestone'); + } +} + +sub _contains_checkin_keyword { + my ($keywords) = @_; + return 0 unless defined $keywords; + + my @keywords + = ref $keywords eq 'ARRAY' ? @$keywords : split(/[\s,]+/, $keywords); + + return scalar grep { lc($_) eq CHECKIN_KEYWORD } @keywords; +} + +1; diff --git a/extensions/MozChangeField/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/MozChangeField/template/en/default/hook/global/user-error-errors.html.tmpl index 62ca30ec20..33019aa691 100644 --- a/extensions/MozChangeField/template/en/default/hook/global/user-error-errors.html.tmpl +++ b/extensions/MozChangeField/template/en/default/hook/global/user-error-errors.html.tmpl @@ -14,4 +14,9 @@ [% title = "Invalid Change" %] You need "editbugs" permissions to alter the '[% field FILTER html %]' field. +[% ELSIF error == "mozchangefield_checkin_needed_tb_requires_milestone" %] + [% title = "Target Milestone Required" %] + A target milestone must be selected when adding the + checkin-needed-tb keyword. + [% END %] diff --git a/t/bmo/checkin-needed-tb-milestone.t b/t/bmo/checkin-needed-tb-milestone.t new file mode 100755 index 0000000000..ad11f883cf --- /dev/null +++ b/t/bmo/checkin-needed-tb-milestone.t @@ -0,0 +1,144 @@ +#!/usr/bin/env perl + +# 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. + +use 5.10.1; + +use strict; +use warnings; + +use lib qw(. lib local/lib/perl5); + +use Test::More; + +require './extensions/MozChangeField/lib/Pre/CheckinNeededTB.pm'; + +{ + package Local::Product; + + sub new { + my ($class, $name) = @_; + return bless {name => $name}, $class; + } + + sub name { + return $_[0]->{name}; + } +} + +{ + package Local::Bug; + + sub new { + my ($class, %args) = @_; + return bless \%args, $class; + } + + sub product_obj { + return Local::Product->new($_[0]->{product}); + } + + sub has_keyword { + return $_[0]->{has_keyword}; + } + + sub target_milestone { + return $_[0]->{milestone}; + } +} + +package main; + +my $rule + = Bugzilla::Extension::MozChangeField::Pre::CheckinNeededTB->new; + +sub evaluate_rule { + my (%args) = @_; + + my $bug = Local::Bug->new( + product => $args{product} || 'Thunderbird', + has_keyword => $args{has_keyword} || 0, + milestone => exists $args{milestone} ? $args{milestone} : '---', + ); + + eval { + $rule->evaluate_set_all({ + bug => $bug, + params => $args{params}, + }); + }; + + return $@; +} + +{ + no warnings qw(once redefine); + + local + *Bugzilla::Extension::MozChangeField::Pre::CheckinNeededTB::ThrowUserError + = sub { die "$_[0]\n"; }; + + foreach my $product ('Calendar', 'MailNews Core', 'Thunderbird') { + like( + evaluate_rule( + product => $product, + params => {keywords => {add => ['checkin-needed-tb']}}, + ), + qr/mozchangefield_checkin_needed_tb_requires_milestone/, + "Adding checkin-needed-tb without a milestone is rejected for $product" + ); + } + + is( + evaluate_rule( + milestone => 'Thunderbird 153', + params => {keywords => {add => ['checkin-needed-tb']}}, + ), + '', + 'An existing target milestone allows the keyword' + ); + + is( + evaluate_rule( + params => { + keywords => {add => ['checkin-needed-tb']}, + target_milestone => 'Thunderbird 153', + }, + ), + '', + 'The keyword and target milestone can be set together' + ); + + is( + evaluate_rule( + product => 'Firefox', + params => {keywords => {add => ['checkin-needed-tb']}}, + ), + '', + 'Other products are unaffected' + ); + + is( + evaluate_rule( + has_keyword => 1, + params => {keywords => {add => ['another-keyword']}}, + ), + '', + 'A bug that already has checkin-needed-tb is unaffected' + ); + + like( + evaluate_rule( + params => {keywords => {set => ['checkin-needed-tb']}}, + ), + qr/mozchangefield_checkin_needed_tb_requires_milestone/, + 'Setting the complete keyword list is also validated' + ); +} + +done_testing();