[Bps-public-commit] RT-Extension-MandatoryOnTransition branch, support-coreroles-and-customroles, created. 0.17-6-geec1d04

Craig Kaiser craig at bestpractical.com
Fri Jan 11 17:14:18 EST 2019


The branch, support-coreroles-and-customroles has been created
        at  eec1d04e1df6fe48d85be4c502704c926cbe4730 (commit)

- Log -----------------------------------------------------------------
commit fa95e1a61afd063b9b70eb7e6c16bec0960d30f9
Author: Craig Kaiser <craig at bestpractical.com>
Date:   Fri Jan 4 11:11:15 2019 -0500

    Support customroles for mandatoryontransition conditions
    
    Use the syntax 'CustomRole.MyRole' to set mandatory conditions for
    customrole values.

diff --git a/lib/RT/Extension/MandatoryOnTransition.pm b/lib/RT/Extension/MandatoryOnTransition.pm
index d238c18..ac96bbd 100644
--- a/lib/RT/Extension/MandatoryOnTransition.pm
+++ b/lib/RT/Extension/MandatoryOnTransition.pm
@@ -127,7 +127,7 @@ config option.  This option takes the generic form of:
 
     Set( %MandatoryOnTransition,
         'QueueName' => {
-            'from -> to' => [ 'BasicField', 'CF.MyField', ],
+            'from -> to' => [ 'BasicField', 'CF.MyField', 'CustomRole.MyRole' ],
         },
     );
 
@@ -146,10 +146,11 @@ Category selection before resolving tickets in every other queue.
 
     Set( %MandatoryOnTransition,
         Helpdesk => {
-            '* -> resolved' => ['TimeWorked', 'CF.Resolution'],
+            '* -> resolved' => ['TimeWorked', 'CF.Resolution', 'CustomRole.Analyst'],
         },
         '*' => {
             '* -> resolved' => ['CF.Category'],
+            'CustomRole.Analyst' => {transition => '* -> open', group => 'Engineering'},
         },
     );
 
@@ -340,6 +341,8 @@ sub RequiredFields {
     my @core = grep { !/^CF\./i && $core_supported{$_} } @$required;
     my @cfs  =  map { /^CF\.(.+)$/i; $1; }
                grep { /^CF\./i } @$required;
+    my @roles = map { /^(:?[CustomRole\.]?.+)$/i; $1; }
+               grep { /^CustomRole\./i } @$required;
 
     # Pull out any must_be or must_not_be rules
     my %cf_must_values = ();
@@ -359,7 +362,25 @@ sub RequiredFields {
             }
         }
     }
-    return (\@core, \@cfs, \%cf_must_values);
+
+    my %role_group_values;
+    foreach my $role (@roles){
+        if ( $config{$role} ){
+            my $transition = $config{$role}->{'transition'};
+            unless ( $transition ){
+                RT->Logger->error("No transition defined in group rules for $role");
+                next;
+            }
+
+            if ( $transition eq "$from -> $to"
+                || $transition eq "* -> $to"
+                || $transition eq "$from -> *" ) {
+
+                $role_group_values{$role} = $config{$role};
+            }
+        }
+    }
+    return (\@core, \@cfs, \@roles, \%cf_must_values, \%role_group_values);
 }
 
 =head3 CheckMandatoryFields
@@ -412,13 +433,15 @@ sub CheckMandatoryFields {
     }
 
     # Some convenience variables set depending on what gets passed
-    my ($CFs, $CurrentUser);
+    my ($CFs, $CRs, $CurrentUser);
     if ( $args{'Ticket'} ){
         $CFs = $args{'Ticket'}->CustomFields;
+        $CRs = $args{'Ticket'}->QueueObj->CustomRoles;
         $CurrentUser = $args{'Ticket'}->CurrentUser();
     }
     elsif ( $args{'Queue'} ){
         $CFs = $args{'Queue'}->TicketCustomFields;
+        $CRs = $args{'Queue'}->CustomRoles;
         $CurrentUser = $args{'Queue'}->CurrentUser();
     }
     else{
@@ -426,15 +449,14 @@ sub CheckMandatoryFields {
         return \@errors;
     }
 
-    my ($core, $cfs, $must_values) = $self->RequiredFields(
+    my ($core, $cfs, $roles, $cf_must_values, $role_group_values) = $self->RequiredFields(
         Ticket  => $args{'Ticket'},
         Queue   => $args{'Queue'} ? $args{'Queue'}->Name : undef,
         From    => $args{'From'},
         To      => $args{'To'},
         NewQueue => $$ARGSRef{'Queue'},
     );
-
-    return \@errors unless @$core or @$cfs;
+    return \@errors unless @$core or @$cfs or $roles;
 
     my $transition =  ($args{'From'} ||'') ne ($args{'To'} || '') ? 'Status' : 'Queue';
 
@@ -500,6 +522,47 @@ sub CheckMandatoryFields {
             $label, $CurrentUser->loc($transition),  $CurrentUser->loc($field_label{$transition}));
     }
 
+    if ( @$roles ) {
+        foreach my $role (@$roles) {
+            my $role_values;
+            my $role_arg = $role;
+            my $role_full = $role;
+
+            if ( not $CRs ){
+                $RT::Logger->error("Custom Roles object required to process mandatory custom roles");
+                return \@errors;
+            }
+            $role =~ qr/^CustomRole\.(.+)$/i;
+            $role = $1;
+
+            my $role_object = RT::CustomRole->new($args{Ticket}->CurrentUser);
+
+            my ($ret, $msg) = $role_object->Load($role);
+            push @errors, $CurrentUser->loc("Failed to load customrole $role:  $msg") unless $ret;
+            return \@errors unless $ret;
+
+            $role_arg = 'RT::CustomRole-' . $role_object->Id;
+
+            ($ret, $msg) = $role_values = $args{Ticket}->RoleGroup($role_object->GroupType);
+            push @errors, $CurrentUser->loc("$msg") unless $ret;
+            return \@errors unless $ret;
+
+            my @role_values;
+            my @row_input_id = grep $role_arg eq $ARGSRef->{$_}, keys %{$ARGSRef};
+            if ( @row_input_id ) {
+                map {push @role_values, $ARGSRef->{"WatcherAddressEmail$_"} } grep { $_ =~ qr/WatcherTypeEmail(.+)/ } @row_input_id;
+            }
+
+            my @temp_array = $role_values->MemberEmailAddresses;
+            push @role_values, @temp_array if scalar @temp_array;
+
+            if ( not scalar @role_values ) {
+                    push @errors, "Requires value for $role to perform action.";
+                    return \@errors;
+            }
+        return \@errors if scalar @errors;
+    }
+
     return \@errors unless @$cfs;
 
     if ( not $CFs ){
@@ -565,7 +628,7 @@ sub CheckMandatoryFields {
         }
 
         # Check for specific values
-        if ( exists $must_values->{$cf->Name} ){
+        if ( exists $cf_must_values->{$cf->Name} ){
             my $cf_value = $value;
 
             if ( not defined $cf_value and $args{'Ticket'} ){
@@ -574,8 +637,8 @@ sub CheckMandatoryFields {
                 $cf_value = $args{'Ticket'}->FirstCustomFieldValue($cf->Name);
             }
 
-            if ( exists $must_values->{$cf->Name}{'must_be'} ){
-                my @must_be = @{$must_values->{$cf->Name}{'must_be'}};
+            if ( exists $cf_must_values->{$cf->Name}{'must_be'} ){
+                my @must_be = @{$cf_must_values->{$cf->Name}{'must_be'}};
 
                 # OK if it's defined and is one of the specified values
                 next if defined $cf_value and grep { $cf_value eq $_ } @must_be;
@@ -593,8 +656,8 @@ sub CheckMandatoryFields {
                 next;
             }
 
-            if ( exists $must_values->{$cf->Name}{'must_not_be'} ){
-                my @must_not_be = @{$must_values->{$cf->Name}{'must_not_be'}};
+            if ( exists $cf_must_values->{$cf->Name}{'must_not_be'} ){
+                my @must_not_be = @{$cf_must_values->{$cf->Name}{'must_not_be'}};
 
                 # OK if it's defined and _not_ in the list
                 next if defined $cf_value and !grep { $cf_value eq $_ } @must_not_be;

commit 6da4599a02eb25995c26537a2e14ae5018c2f0ce
Author: Craig Kaiser <craig at bestpractical.com>
Date:   Fri Jan 11 11:29:59 2019 -0500

    Support core roles for mandatoryontransation

diff --git a/lib/RT/Extension/MandatoryOnTransition.pm b/lib/RT/Extension/MandatoryOnTransition.pm
index ac96bbd..79aace9 100644
--- a/lib/RT/Extension/MandatoryOnTransition.pm
+++ b/lib/RT/Extension/MandatoryOnTransition.pm
@@ -342,7 +342,7 @@ sub RequiredFields {
     my @cfs  =  map { /^CF\.(.+)$/i; $1; }
                grep { /^CF\./i } @$required;
     my @roles = map { /^(:?[CustomRole\.]?.+)$/i; $1; }
-               grep { /^CustomRole\./i } @$required;
+               grep { /^CustomRole\.|^AdminCc|^Cc|^Requestor/i } @$required;
 
     # Pull out any must_be or must_not_be rules
     my %cf_must_values = ();
@@ -528,24 +528,31 @@ sub CheckMandatoryFields {
             my $role_arg = $role;
             my $role_full = $role;
 
-            if ( not $CRs ){
-                $RT::Logger->error("Custom Roles object required to process mandatory custom roles");
-                return \@errors;
-            }
-            $role =~ qr/^CustomRole\.(.+)$/i;
-            $role = $1;
+            if ( $role =~ 'CustomRole.' ) {
+                if ( not $CRs ){
+                    $RT::Logger->error("Custom Roles object required to process mandatory custom roles");
+                    return \@errors;
+                }
+                $role =~ qr/^CustomRole\.(.+)$/i;
+                $role = $1;
 
-            my $role_object = RT::CustomRole->new($args{Ticket}->CurrentUser);
+                my $role_object = RT::CustomRole->new($args{Ticket}->CurrentUser);
 
-            my ($ret, $msg) = $role_object->Load($role);
-            push @errors, $CurrentUser->loc("Failed to load customrole $role:  $msg") unless $ret;
-            return \@errors unless $ret;
+                my ($ret, $msg) = $role_object->Load($role);
+                push @errors, $CurrentUser->loc("Failed to load customrole $role:  $msg") unless $ret;
+                return \@errors unless $ret;
 
-            $role_arg = 'RT::CustomRole-' . $role_object->Id;
+                $role_arg = 'RT::CustomRole-' . $role_object->Id;
 
-            ($ret, $msg) = $role_values = $args{Ticket}->RoleGroup($role_object->GroupType);
-            push @errors, $CurrentUser->loc("$msg") unless $ret;
-            return \@errors unless $ret;
+                ($ret, $msg) = $role_values = $args{Ticket}->RoleGroup($role_object->GroupType);
+                push @errors, $CurrentUser->loc("Could not load current user") unless $ret;
+                return \@errors unless $ret;
+            } else {
+                $role_values = RT::Group->new($args{Ticket}->CurrentUser);
+                my ($ret, $msg) = $role_values->LoadRoleGroup(Object => $args{Ticket}, Name => $role);
+                push @errors, $CurrentUser->loc("Failed to load role $role for ticket: $msg") unless $ret;
+                return \@errors unless $ret;
+            }
 
             my @role_values;
             my @row_input_id = grep $role_arg eq $ARGSRef->{$_}, keys %{$ARGSRef};
@@ -557,7 +564,8 @@ sub CheckMandatoryFields {
             push @role_values, @temp_array if scalar @temp_array;
 
             if ( not scalar @role_values ) {
-                    push @errors, "Requires value for $role to perform action.";
+                    push @errors, $CurrentUser->loc("[_1] is required when changing [_2] to [_3]",
+                        $role, $CurrentUser->loc("$transition"), $CurrentUser->loc($args{To}));
                     return \@errors;
             }
         return \@errors if scalar @errors;

commit 3bb91bedf24def3a2ccaf1450e73bcd1d1dca525
Author: Craig Kaiser <craig at bestpractical.com>
Date:   Fri Jan 4 14:33:30 2019 -0500

    Allow group key to be set for roles
    
    If a group key is set for a role it will be attempted
    to be loaded and then check that a value for the
    role is a member of the group.

diff --git a/lib/RT/Extension/MandatoryOnTransition.pm b/lib/RT/Extension/MandatoryOnTransition.pm
index 79aace9..6aa7eaa 100644
--- a/lib/RT/Extension/MandatoryOnTransition.pm
+++ b/lib/RT/Extension/MandatoryOnTransition.pm
@@ -567,7 +567,30 @@ sub CheckMandatoryFields {
                     push @errors, $CurrentUser->loc("[_1] is required when changing [_2] to [_3]",
                         $role, $CurrentUser->loc("$transition"), $CurrentUser->loc($args{To}));
                     return \@errors;
+            } elsif ( $role_group_values->{$role_full}->{group} ) {
+                my $group_name = $role_group_values->{$role_full}->{group};
+                my $group = RT::Group->new($args{Ticket}->CurrentUser);
+
+                my ($ret, $msg) = $group->LoadUserDefinedGroup($group_name);
+                push @errors, $CurrentUser->loc("Failed to load group: '$group_name' $msg") unless $ret;
+                return \@errors unless $ret;
+
+                my $has_valid_member;
+                my $user = RT::User->new($args{Ticket}->CurrentUser);
+
+                foreach my $member (@role_values) {
+                    next if $has_valid_member;
+                    ($ret, $msg) = $user->LoadByEmail($member);
+                    RT::Logger->error($msg) unless $ret;
+                    next unless $ret;
+
+                    $has_valid_member = $group->HasMemberRecursively($user->Id);
+                }
+                push @errors, $CurrentUser->loc("User who is a member of $group_name is required for role: $role")
+                    unless $has_valid_member;
+                return \@errors unless $has_valid_member;
             }
+        }
         return \@errors if scalar @errors;
     }
 

commit aef2cc5813a15deb967e9068678332206b3753ce
Author: Craig Kaiser <craig at bestpractical.com>
Date:   Mon Jan 7 08:37:09 2019 -0500

    Move Owner code into the roles code block
    
    Since owner is a ticket role it makes logical sense to move the code
    handling the owner value on a ticket into the same code block as
    the other role handling code.

diff --git a/lib/RT/Extension/MandatoryOnTransition.pm b/lib/RT/Extension/MandatoryOnTransition.pm
index 6aa7eaa..4b006bd 100644
--- a/lib/RT/Extension/MandatoryOnTransition.pm
+++ b/lib/RT/Extension/MandatoryOnTransition.pm
@@ -258,18 +258,16 @@ pair to %CORE_FOR_UPDATE and/or %CORE_FOR_CREATE.
 
 =cut
 
-our @CORE_SUPPORTED  = qw(Content TimeWorked TimeTaken Owner);
-our @CORE_TICKET     = qw(TimeWorked Owner);
+our @CORE_SUPPORTED  = qw(Content TimeWorked TimeTaken);
+our @CORE_TICKET     = qw(TimeWorked);
 our %CORE_FOR_UPDATE = (
     TimeWorked  => 'UpdateTimeWorked',
     TimeTaken   => 'UpdateTimeWorked',
-    Content     => 'UpdateContent',
-    Owner       => 'Owner',
+    Content     => 'UpdateContent'
 );
 our %CORE_FOR_CREATE = (
     TimeWorked  => 'TimeWorked',
     Content     => 'Content',
-    Owner       => 'Owner',
 );
 
 =head2 Methods
@@ -342,7 +340,7 @@ sub RequiredFields {
     my @cfs  =  map { /^CF\.(.+)$/i; $1; }
                grep { /^CF\./i } @$required;
     my @roles = map { /^(:?[CustomRole\.]?.+)$/i; $1; }
-               grep { /^CustomRole\.|^AdminCc|^Cc|^Requestor/i } @$required;
+               grep { /^CustomRole\.|^AdminCc|^Cc|^Requestor|^Owner/i } @$required;
 
     # Pull out any must_be or must_not_be rules
     my %cf_must_values = ();
@@ -479,36 +477,6 @@ sub CheckMandatoryFields {
             : $CORE_FOR_CREATE{$field};
         next unless $arg;
 
-        if ($field eq 'Owner') {
-            my $value;
-
-            # There are 2 Owner fields on Jumbo page, copied the same handling from it.
-            if (ref $ARGSRef->{$arg}) {
-                foreach my $owner (@{$ARGSRef->{$arg}}) {
-                    if (defined($owner) && $owner =~ /\D/) {
-                        $value = $owner unless ($args{'Ticket'}->OwnerObj->Name eq $owner);
-                    }
-                    elsif (length $owner) {
-                        $value = $owner unless ($args{'Ticket'}->OwnerObj->id == $owner);
-                    }
-                }
-            }
-            else {
-                $value = $ARGSRef->{$arg};
-            }
-
-            if (($value || $args{'Ticket'}->$field()) == $RT::Nobody->id) {
-                push @errors,
-                  $CurrentUser->loc(
-                    "[_1] is required when changing [_2] to [_3]",
-                    $field,
-                    $CurrentUser->loc($transition),
-                    $CurrentUser->loc($field_label{$transition})
-                  );
-                next;
-            }
-        }
-
         next if defined $ARGSRef->{$arg} and length $ARGSRef->{$arg};
 
         # Do we have a value currently?
@@ -547,21 +515,54 @@ sub CheckMandatoryFields {
                 ($ret, $msg) = $role_values = $args{Ticket}->RoleGroup($role_object->GroupType);
                 push @errors, $CurrentUser->loc("Could not load current user") unless $ret;
                 return \@errors unless $ret;
+            } elsif ( $role eq 'Owner' ) {
+                my $value;
+                # There are 2 Owner fields on Jumbo page, copied the same handling from it.
+                if (ref $ARGSRef->{$role}) {
+                    foreach my $owner (@{$ARGSRef->{$role}}) {
+                        if (defined($owner) && $owner =~ /\D/) {
+                            $value = $owner unless ($args{'Ticket'}->OwnerObj->Name eq $owner);
+                        } elsif (length $owner) {
+                            $value = $owner unless ($args{'Ticket'}->OwnerObj->id == $owner);
+                        }
+                     }
+                } else {
+                    $value = $ARGSRef->{$role};
+                }
+                if (($value || $args{'Ticket'}->$role()) == $RT::Nobody->id) {
+                    push @errors,
+                    $CurrentUser->loc(
+                        "[_1] is required when changing [_2] to [_3]",
+                        $role,
+                        $CurrentUser->loc($transition),
+                        $CurrentUser->loc($args{To})
+                    );
+                } else {
+                    my $user = RT::User->new($args{Ticket}->CurrentUser);
+                    my ($ret, $msg) = $user->Load($value);
+                    push @errors, $CurrentUser->loc("Could not load user: $value") unless $ret;
+                    return \@errors unless $ret;
+
+                    $role_values = $user->EmailAddress;
+                }
             } else {
                 $role_values = RT::Group->new($args{Ticket}->CurrentUser);
                 my ($ret, $msg) = $role_values->LoadRoleGroup(Object => $args{Ticket}, Name => $role);
                 push @errors, $CurrentUser->loc("Failed to load role $role for ticket: $msg") unless $ret;
                 return \@errors unless $ret;
             }
-
             my @role_values;
             my @row_input_id = grep $role_arg eq $ARGSRef->{$_}, keys %{$ARGSRef};
             if ( @row_input_id ) {
                 map {push @role_values, $ARGSRef->{"WatcherAddressEmail$_"} } grep { $_ =~ qr/WatcherTypeEmail(.+)/ } @row_input_id;
             }
 
-            my @temp_array = $role_values->MemberEmailAddresses;
-            push @role_values, @temp_array if scalar @temp_array;
+            if ( ref $role_values eq 'RT::Group' ) {
+                my @temp_array = $role_values->MemberEmailAddresses;
+                push @role_values, @temp_array if scalar @temp_array;
+            } else {
+                push @role_values, $role_values;
+            }
 
             if ( not scalar @role_values ) {
                     push @errors, $CurrentUser->loc("[_1] is required when changing [_2] to [_3]",

commit 872c6d222030337c170efe94b3b6621d5601482f
Author: Craig Kaiser <craig at bestpractical.com>
Date:   Fri Jan 11 11:37:10 2019 -0500

    Update README

diff --git a/README b/README
index ad7e034..80db750 100644
--- a/README
+++ b/README
@@ -96,7 +96,7 @@ CONFIGURATION
 
         Set( %MandatoryOnTransition,
             'QueueName' => {
-                'from -> to' => [ 'BasicField', 'CF.MyField', ],
+                'from -> to' => [ 'BasicField', 'CF.MyField', 'CustomRole.MyRole' ],
             },
         );
 
@@ -125,6 +125,18 @@ CONFIGURATION
     The transition syntax is similar to that found in RT's Lifecycles. See
     perldoc /opt/rt4/etc/RT_Config.pm.
 
+  Requiring role values
+    You can require any core or custom role on a RT::Ticket object, further
+    more you can require a 'group' category to check that at least one value
+    for that role is a member of the required group. In the below example we
+    require that the custom role 'customer' have a user value who is a
+    member of the group 'Customers' and the owner of the ticket must be a
+    member of the group 'Helpdesk'.
+
+    Set( %MandatoryOnTransition, 'General' => { 'CustomRole.customer' => {
+    transition => 'open -> *', group => 'Customers' }, 'Owner' => {
+    transition => 'open -> *', group => 'Helpdesk' }, }, );
+
   Restrictions on Queue Transitions
     The default behavior for MandatoryOnTransition operates on status
     transitions, so a change from new to open or from open to resolved. It
diff --git a/lib/RT/Extension/MandatoryOnTransition.pm b/lib/RT/Extension/MandatoryOnTransition.pm
index 4b006bd..46e6167 100644
--- a/lib/RT/Extension/MandatoryOnTransition.pm
+++ b/lib/RT/Extension/MandatoryOnTransition.pm
@@ -146,17 +146,31 @@ Category selection before resolving tickets in every other queue.
 
     Set( %MandatoryOnTransition,
         Helpdesk => {
-            '* -> resolved' => ['TimeWorked', 'CF.Resolution', 'CustomRole.Analyst'],
+            '* -> resolved' => ['TimeWorked', 'CF.Resolution'],
         },
         '*' => {
             '* -> resolved' => ['CF.Category'],
-            'CustomRole.Analyst' => {transition => '* -> open', group => 'Engineering'},
         },
     );
 
 The transition syntax is similar to that found in RT's Lifecycles.  See
 C<perldoc /opt/rt4/etc/RT_Config.pm>.
 
+=head2 Requiring role values
+
+You can require any core or custom role on a RT::Ticket object, further more
+you can require a 'group' category to check that at least one value for that
+role is a member of the required group. In the below example we require that
+the custom role 'customer' have a user value who is a member of the group 'Customers'
+and the owner of the ticket must be a member of the group 'Helpdesk'.
+
+Set( %MandatoryOnTransition,
+    'General' => {
+        'CustomRole.customer' => { transition => 'open -> *', group => 'Customers' },
+        'Owner' => { transition => 'open -> *', group => 'Helpdesk' },
+    },
+);
+
 =head2 Restrictions on Queue Transitions
 
 The default behavior for C<MandatoryOnTransition> operates on status transitions,

commit eec1d04e1df6fe48d85be4c502704c926cbe4730
Author: Craig Kaiser <craig at bestpractical.com>
Date:   Fri Jan 11 16:59:07 2019 -0500

    Create test file for role mandatory fields

diff --git a/xt/require_owner_for_resolve.t b/xt/require_owner_for_resolve.t
deleted file mode 100644
index 20fd377..0000000
--- a/xt/require_owner_for_resolve.t
+++ /dev/null
@@ -1,122 +0,0 @@
-use strict;
-use warnings;
-
-use RT::Extension::MandatoryOnTransition::Test tests => undef, config => <<CONFIG
-Set( %MandatoryOnTransition,
-     '*' => {
-         '* -> resolved' => ['Owner',],
-     }
-    );
-CONFIG
-  ;
-
-use_ok('RT::Extension::MandatoryOnTransition');
-
-my ($baseurl, $m) = RT::Test->started_ok();
-
-ok($m->login('root', 'password'), 'logged in');
-$m->get_ok($m->rt_base_url);
-
-my $root = RT::User->new(RT->SystemUser);
-$root->Load('root');
-ok($root->id, 'Loaded root');
-
-diag "Resolve ticket through Update with required Owner";
-{
-    my $t = RT::Test->create_ticket(
-        Queue   => 'General',
-        Subject => 'Test Mandatory Owner On Resolve',
-        Content => 'Testing',
-    );
-
-    ok($t->id, 'Created test ticket: ' . $t->id);
-
-    $m->goto_ticket($t->id);
-
-    $m->follow_link_ok({ text => 'Resolve' }, 'Try to resolve ticket');
-    $m->text_contains('Test Mandatory Owner On Resolve');
-    $m->submit_form_ok(
-        {   form_name => 'TicketUpdate',
-            button    => 'SubmitTicket',
-        },
-        'Submit resolve with no Owner set'
-    );
-    $m->text_contains('Owner is required when changing Status to resolved');
-    $m->submit_form_ok(
-        {   form_name => 'TicketUpdate',
-            button    => 'SubmitTicket',
-            fields    => { Owner => $root->id }
-        },
-        'Submit resolve with Owner set'
-    );
-    $m->text_lacks('Owner is required when changing Status to resolved');
-    $m->text_contains('Owner changed from Nobody to root');
-    $m->text_contains("Status changed from 'new' to 'resolved'");
-}
-
-diag "Resolve ticket through Basics with required Owner";
-{
-    my $t = RT::Test->create_ticket(
-        Queue   => 'General',
-        Subject => 'Test Mandatory Owner On Resolve',
-        Content => 'Testing',
-    );
-    ok($t->id, 'Created ticket to resolve through Modify.html: ' . $t->id);
-
-    $m->goto_ticket($t->id);
-    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
-    $m->submit_form_ok(
-        {   form_name => 'TicketModify',
-            fields    => { Status => 'resolved', },
-            button    => 'SubmitTicket',
-        },
-        'Submit resolve with no Owner set'
-    );
-    $m->text_contains('Owner is required when changing Status to resolved');
-
-    $m->submit_form_ok(
-        {   form_name => 'TicketModify',
-            fields    => { Status => 'resolved', Owner => $root->id, },
-            button    => 'SubmitTicket',
-        },
-        'Submit resolve with no Owner set'
-    );
-    $m->text_lacks('Owner is required when changing Status to resolved');
-    $m->text_contains('Owner changed from Nobody to root');
-    $m->text_contains("Status changed from 'new' to 'resolved'");
-}
-
-diag "Resolve ticket through Jumbo with required Owner";
-{
-    my $t = RT::Test->create_ticket(
-        Queue   => 'General',
-        Subject => 'Test Mandatory Owner On Resolve',
-        Content => 'Testing',
-    );
-    ok($t->id, 'Created ticket to resolve through ModifyAll.html: ' . $t->id);
-
-    $m->goto_ticket($t->id);
-    $m->follow_link_ok({ text => 'Jumbo' }, 'Get ModifyAll.html of ticket');
-    $m->submit_form_ok(
-        {   form_name => 'TicketModifyAll',
-            fields    => { Status => 'resolved', },
-            button    => 'SubmitTicket',
-        },
-        'Submit resolve with no Owner set'
-    );
-    $m->text_contains('Owner is required when changing Status to resolved');
-
-    $m->submit_form_ok(
-        {   form_name => 'TicketModifyAll',
-            fields    => { Status => 'resolved', Owner => $root->id, },
-            button    => 'SubmitTicket',
-        },
-        'Submit resolve with no Owner set'
-    );
-    $m->text_lacks('Owner is required when changing Status to resolved');
-    $m->text_contains('Owner changed from Nobody to root');
-    $m->text_contains("Status changed from 'new' to 'resolved'");
-}
-
-undef $m;
-done_testing;
diff --git a/xt/roles.t b/xt/roles.t
new file mode 100644
index 0000000..71a975e
--- /dev/null
+++ b/xt/roles.t
@@ -0,0 +1,260 @@
+use strict;
+use warnings;
+
+use RT::Extension::MandatoryOnTransition::Test tests => undef, config => <<CONFIG
+Set( %MandatoryOnTransition,
+     '*' => {
+         '* -> resolved'  => ['Owner',],
+         '* -> stalled'   => ['AdminCc'],
+         '* -> deleted'   => ['CustomRole.vip'],
+         'AdminCc'        => { transition => '* -> stalled', group => 'Admins' },
+         'CustomRole.vip' => { transition => '* -> deleted' },
+        }
+    );
+CONFIG
+  ;
+
+use_ok('RT::Extension::MandatoryOnTransition');
+
+my ($baseurl, $m) = RT::Test->started_ok();
+
+ok($m->login('root', 'password'), 'logged in');
+$m->get_ok($m->rt_base_url);
+
+my $root = RT::User->new(RT->SystemUser);
+$root->Load('root');
+ok($root->id, 'Loaded root');
+
+diag "Resolve ticket through Update with required Owner";
+{
+    my $t = RT::Test->create_ticket(
+        Queue   => 'General',
+        Subject => 'Test Mandatory Owner On Resolve',
+        Content => 'Testing',
+    );
+
+    ok($t->id, 'Created test ticket: ' . $t->id);
+
+    $m->goto_ticket($t->id);
+
+    $m->follow_link_ok({ text => 'Resolve' }, 'Try to resolve ticket');
+    $m->text_contains('Test Mandatory Owner On Resolve');
+    $m->submit_form_ok(
+        {   form_name => 'TicketUpdate',
+            button    => 'SubmitTicket',
+        },
+        'Submit resolve with no Owner set'
+    );
+    $m->text_contains('Owner is required when changing Status to resolved');
+    $m->submit_form_ok(
+        {   form_name => 'TicketUpdate',
+            button    => 'SubmitTicket',
+            fields    => { Owner => $root->id }
+        },
+        'Submit resolve with Owner set'
+    );
+    $m->text_lacks('Owner is required when changing Status to resolved');
+    $m->text_contains('Owner changed from Nobody to root');
+    $m->text_contains("Status changed from 'new' to 'resolved'");
+}
+
+diag "Resolve ticket through Basics with required Owner";
+{
+    my $t = RT::Test->create_ticket(
+        Queue   => 'General',
+        Subject => 'Test Mandatory Owner On Resolve',
+        Content => 'Testing',
+    );
+    ok($t->id, 'Created ticket to resolve through Modify.html: ' . $t->id);
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'resolved', },
+            button    => 'SubmitTicket',
+        },
+        'Submit resolve with no Owner set'
+    );
+    $m->text_contains('Owner is required when changing Status to resolved');
+
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'resolved', Owner => $root->id, },
+            button    => 'SubmitTicket',
+        },
+        'Submit resolve with no Owner set'
+    );
+    $m->text_lacks('Owner is required when changing Status to resolved');
+    $m->text_contains('Owner changed from Nobody to root');
+    $m->text_contains("Status changed from 'new' to 'resolved'");
+}
+
+diag "Resolve ticket through Jumbo with required Owner";
+{
+    my $t = RT::Test->create_ticket(
+        Queue   => 'General',
+        Subject => 'Test Mandatory Owner On Resolve',
+        Content => 'Testing',
+    );
+    ok($t->id, 'Created ticket to resolve through ModifyAll.html: ' . $t->id);
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Jumbo' }, 'Get ModifyAll.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModifyAll',
+            fields    => { Status => 'resolved', },
+            button    => 'SubmitTicket',
+        },
+        'Submit resolve with no Owner set'
+    );
+    $m->text_contains('Owner is required when changing Status to resolved');
+
+    $m->submit_form_ok(
+        {   form_name => 'TicketModifyAll',
+            fields    => { Status => 'resolved', Owner => $root->id, },
+            button    => 'SubmitTicket',
+        },
+        'Submit resolve with no Owner set'
+    );
+    $m->text_lacks('Owner is required when changing Status to resolved');
+    $m->text_contains('Owner changed from Nobody to root');
+    $m->text_contains("Status changed from 'new' to 'resolved'");
+}
+
+diag "Test core role fields";
+{
+    my $role = qw/AdminCc/;
+    my $t = RT::Test->create_ticket(
+        Queue   => 'General',
+        Subject => 'Test Mandatory Owner On Resolve',
+        Content => 'Testing',
+    );
+    ok($t->id, 'Created test ticket: ' . $t->id);
+    my ($ret, $msg) = $t->SetStatus('open');
+    ok $ret, $msg;
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'stalled', },
+            button    => 'SubmitTicket',
+        },
+        "Submit stalled with no $role member"
+    );
+    $m->text_contains("$role is required when changing Status to stalled");
+
+    my $role_group = $t->$role;
+    ok $role_group->Id;
+
+    my $root = RT::User->new(RT->SystemUser);
+    $root->Load('root');
+    ok($root->id, 'Loaded root');
+
+    ($ret, $msg) = $role_group->AddMember($root->PrincipalId);
+    ok $ret, $msg;
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'stalled', },
+            button    => 'SubmitTicket',
+        },
+        "Try to stall ticket with no Admins group created"
+    );
+    $m->text_contains('Failed to load group: \'Admins\' Couldn\'t find row');
+
+    my $group = RT::Group->new(RT->SystemUser);
+    ($ret, $msg) = $group->CreateUserDefinedGroup(Name => 'Admins');
+    ok $ret, "Failed to create Amdins group: $msg";
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'stalled', },
+            button    => 'SubmitTicket',
+        },
+        "Try to stall ticket with no $role but not a member of required group"
+    );
+    $m->text_contains("User who is a member of Admins is required for role: $role");
+
+    ($ret, $msg) = $group->AddMember($root->PrincipalId);
+    ok $ret, $msg;
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'stalled', },
+            button    => 'SubmitTicket',
+        },
+        "Try to stall ticket with $role and  group required"
+    );
+    $m->text_contains("Status changed from 'open' to 'stalled'");
+}
+
+diag "Test customrole mandatory fields";
+{
+    my $t = RT::Test->create_ticket(
+        Queue   => 'General',
+        Subject => 'Test Mandatory Owner On Resolve',
+        Content => 'Testing',
+    );
+    ok($t->id, 'Created test ticket: ' . $t->id);
+    my ($ret, $msg) = $t->SetStatus('open');
+    ok $ret, $msg;
+
+    my $id = $t->id;
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'deleted', },
+            button    => 'SubmitTicket',
+        },
+        "Submit deleted with customrole 'vip' created"
+    );
+    $m->text_contains("Failed to load customrole vip: Couldn't find row");
+
+    my $customrole = RT::CustomRole->new(RT->SystemUser);
+    ($ret, $msg) = $customrole->Create(Name => 'vip');
+    ok $ret, $msg;
+
+    ($ret, $msg) = $customrole->Load('vip');
+    ok $ret, $msg;
+
+    ($ret, $msg) = $customrole->AddToObject($t->Queue);
+    ok $ret, $msg;
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'deleted', },
+            button    => 'SubmitTicket',
+        },
+        "Submit deleted with no value for required customrole"
+    );
+    $m->text_contains("vip is required when changing Status to deleted");
+
+    ($ret, $msg) = $t->AddWatcher(Type => $customrole->GroupType, Principal => $root->PrincipalObj);
+    ok $ret, $msg;
+
+    $m->goto_ticket($t->id);
+    $m->follow_link_ok({ text => 'Basics' }, 'Get Modify.html of ticket');
+    $m->submit_form_ok(
+        {   form_name => 'TicketModify',
+            fields    => { Status => 'deleted', },
+            button    => 'SubmitTicket',
+        },
+        "Submit deleted with manatory customrole requirements met."
+    );
+    $m->text_contains("Ticket deleted");
+}
+
+undef $m;
+done_testing;

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list