[Rt-commit] rt branch, 4.4/custom-roles, repushed

Shawn Moore shawn at bestpractical.com
Tue Oct 27 21:02:47 EDT 2015


The branch 4.4/custom-roles was deleted and repushed:
       was 1a8e3a17877b5bbbbf86c68cade97267b43cc0a7
       now 6c22fa9242076a6863b2e15d6839c51910ede898

 1:  3da168a =  1:  3da168a Tidy EmailInput and avoid including unescaped parameters
 2:  f4a3966 =  2:  f4a3966 Placeholder and EntryHint for EmailInput
 3:  5b777ea =  3:  5b777ea Support autocomplete-return for EmailInput
 4:  1168416 =  4:  1168416 Support for (by default, off) autocomplete of Nobody and System
 5:  87d20c2 =  5:  87d20c2 Only include one copy of "check box to delete" for queue watchers
 6:  7c59fae =  6:  7c59fae Simplify IsManageableRoleGroupType
 7:  78cbe3d =  7:  78cbe3d Switch several hardcoded lists to use ->Roles
 8:  8b8897b =  8:  8b8897b Avoid empty results and undef warnings in bulk update
 9:  89d776e =  9:  89d776e Tidy AddWatchers
10:  accaf5b = 10:  accaf5b Use the record we have for inspecting roles, rather than class RT::Queue
11:  3d5ba26 = 11:  3d5ba26 Add a CheckRight param to ->RoleGroup
12:  f467d55 = 12:  f467d55 Fix one-off "Administrative Cc" with "AdminCc"
13:  7cb01e5 = 13:  7cb01e5 Provide queue to EditBasics for new tickets (they don't have a ->QueueObj)
14:  71fc832 = 14:  71fc832 Provide defaults to EditBasics for "Add More Attach" etc
15:  4b1fd93 = 15:  4b1fd93 Factor out a CanonicalizePrincipal from AddRoleMember
16:  c7759ba = 16:  c7759ba Consistent error message across AddRoleMember and DeleteRoleMember
17:  ff898d8 = 17:  ff898d8 Factor out a _CreateRoleGroup
18:  6f0d646 = 18:  6f0d646 Lazily create ticket role groups if needed
19:  b2d8700 = 19:  b2d8700 Use queue to resolve roles on create since the ticket isn't created yet
20:  fe79a2f = 20:  fe79a2f Process watcher updates from ticket Modify
21:  eccef99 ! 21:  e5dda97 Improve messaging around updating queue/ticket watchers
    @@ -55,12 +55,15 @@
     +
     +    my $original_user;
     +    my $group = $self->RoleGroup( $args{Type} );
    -+    if ($group->SingleMemberRoleGroup) {
    ++    if ($group->id && $group->SingleMemberRoleGroup) {
     +        my $users = $group->UserMembersObj( Recursively => 0 );
     +        $original_user = $users->First;
     +        if ($original_user->PrincipalId == $principal->Id) {
     +            return 1;
     +        }
    ++    }
    ++    else {
    ++        $original_user = RT->Nobody;
     +    }
     +
     +    ((my $ok), $msg) = $self->AddRoleMember(
22:  4a6331d = 22:  5539d9a Additional option for roles
23:  f7be7cd = 23:  34bfce3 Tidy watcher searching in RT::Tickets
24:  dbea774 = 24:  f0acbba Improve comments around GetPrincipalsMap
25:  a08feeb = 25:  cdf1d2e Add Ticket->RoleAddresses($Name)
26:  a6cd627 ! 26:  524f3d8 Add a Group->Label hook for when we display a group's name in the UI
    @@ -53,6 +53,19 @@
                      ];
                  }
              }
    +
    +diff --git a/lib/RT/Principal.pm b/lib/RT/Principal.pm
    +--- a/lib/RT/Principal.pm
    ++++ b/lib/RT/Principal.pm
    +@@
    +     return $self->Object->InstanceObj->Name if ($self->Object->Domain eq 'ACLEquivalence');
    + 
    +     # Otherwise, show the group name
    +-    return $self->Object->Name;
    ++    return $self->Object->Label;
    + }
    + 
    + =head2 GrantRight  { Right => RIGHTNAME, Object => undef }
     
     diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm
     --- a/lib/RT/Queue.pm
27:  01dc0ac = 27:  baa947b Switch to ->LabelForRole
28:  e7b0433 ! 28:  991f84a Add custom roles for queues
    @@ -1014,6 +1014,66 @@
     +
     +=cut
     +
    ++sub _SetGroupsDisabledForQueue {
    ++    my $self = shift;
    ++    my $value = shift;
    ++    my $queue = shift;
    ++
    ++    # set disabled on the queue group
    ++    my $queue_group = RT::Group->new($self->CurrentUser);
    ++    $queue_group->LoadRoleGroup(
    ++        Name   => $self->GroupType,
    ++        Object => $queue,
    ++    );
    ++
    ++    if (!$queue_group->Id) {
    ++        $RT::Handle->Rollback;
    ++        $RT::Logger->error("Couldn't find role group for " . $self->GroupType . " on queue " . $queue->Id);
    ++        return(undef);
    ++    }
    ++
    ++    my ($ok, $msg) = $queue_group->SetDisabled($value);
    ++    unless ($ok) {
    ++        $RT::Handle->Rollback;
    ++        $RT::Logger->error("Couldn't SetDisabled($value) on role group: $msg");
    ++        return(undef);
    ++    }
    ++
    ++    # disable each existant ticket group
    ++    my $ticket_groups = RT::Groups->new($self->CurrentUser);
    ++
    ++    if ($value) {
    ++        $ticket_groups->LimitToEnabled;
    ++    }
    ++    else {
    ++        $ticket_groups->LimitToDeleted;
    ++    }
    ++
    ++    $ticket_groups->Limit(FIELD => 'Domain', OPERATOR => 'LIKE', VALUE => "RT::Ticket-Role", CASESENSITIVE => 0 );
    ++    $ticket_groups->Limit(FIELD => 'Name', OPERATOR => '=', VALUE => $self->GroupType, CASESENSITIVE => 0);
    ++
    ++    my $tickets = $ticket_groups->Join(
    ++        ALIAS1 => 'main',
    ++        FIELD1 => 'Instance',
    ++        TABLE2 => 'Tickets',
    ++        FIELD2 => 'Id',
    ++    );
    ++    $ticket_groups->Limit(
    ++        ALIAS => $tickets,
    ++        FIELD => 'Queue',
    ++        VALUE => $queue->Id,
    ++    );
    ++
    ++    while (my $ticket_group = $ticket_groups->Next) {
    ++        my ($ok, $msg) = $ticket_group->SetDisabled($value);
    ++        unless ($ok) {
    ++            $RT::Handle->Rollback;
    ++            $RT::Logger->error("Couldn't SetDisabled($value) ticket role group: $msg");
    ++            return(undef);
    ++        }
    ++    }
    ++}
    ++
     +sub SetDisabled {
     +    my $self = shift;
     +    my $value = shift;
    @@ -1027,21 +1087,13 @@
     +        return ($ok, $msg);
     +    }
     +
    -+    my $groups = RT::Groups->new($self->CurrentUser);
    -+    if ($value) {
    -+        # if we're disabling, only need to update enabled groups
    -+        $groups->LimitToEnabled;
    -+    }
    -+    else {
    -+        $groups->LimitToDeleted;
    -+    }
    -+
    -+    # disable all groups for this role, so they no longer grant privileges
    -+    $groups->Limit(FIELD => 'Domain',   OPERATOR => 'LIKE', VALUE => "%-Role", CASESENSITIVE => 0 );
    -+    $groups->Limit(FIELD => 'Name',     OPERATOR => '=',    VALUE => $self->GroupType, CASESENSITIVE => 0);
    -+
    -+    while (my $group = $groups->Next) {
    -+        $group->SetDisabled($value);
    ++    # we can't unconditionally re-enable all role groups because
    ++    # if you add a role to queues A and B, add users and privileges and
    ++    # tickets on both, remove the role from B, disable the role, then re-enable
    ++    # the role, we shouldn't re-enable B because it's still removed
    ++    my $queues = $self->AddedTo;
    ++    while (my $queue = $queues->Next) {
    ++        $self->_SetGroupsDisabledForQueue($value, $queue);
     +    }
     +
     +    $RT::Handle->Commit();
    @@ -1533,6 +1585,8 @@
     +
     +=head2 Add
     +
    ++Adds the custom role to the queue and creates (or re-enables) that queue's role
    ++group.
     +
     +=cut
     +
    @@ -1559,7 +1613,14 @@
     +        Object => $queue,
     +    );
     +
    -+    if (!$existing->Id) {
    ++    if ($existing->Id) {
    ++        # there already was a role group for this queue, which means
    ++        # this was previously added, then removed, and is now being re-added,
    ++        # which means we have to re-enable the queue group and all the
    ++        # ticket groups
    ++        $role->_SetGroupsDisabledForQueue(0, $queue);
    ++    }
    ++    else {
     +        my $group = RT::Group->new($self->CurrentUser);
     +        my ($ok, $msg) = $group->CreateRoleGroup(
     +            Name   => $role->GroupType,
    @@ -1571,6 +1632,33 @@
     +            $RT::Logger->error("Couldn't create a role group: $msg");
     +            return(undef);
     +        }
    ++    }
    ++
    ++    $RT::Handle->Commit;
    ++
    ++    return ($ok, $msg);
    ++}
    ++
    ++
    ++=head2 Delete
    ++
    ++Removes the custom role from the queue and disables that queue's role group.
    ++
    ++=cut
    ++
    ++sub Delete {
    ++    my $self = shift;
    ++
    ++    $RT::Handle->BeginTransaction;
    ++
    ++    $self->CustomRoleObj->_SetGroupsDisabledForQueue(1, $self->QueueObj);
    ++
    ++    # remove the ObjectCustomRole record
    ++    my ($ok, $msg) = $self->SUPER::Delete(@_);
    ++    unless ($ok) {
    ++        $RT::Handle->Rollback;
    ++        $RT::Logger->error("Couldn't add ObjectCustomRole: $msg");
    ++        return(undef);
     +    }
     +
     +    $RT::Handle->Commit;
    @@ -3209,20 +3297,17 @@
     +
     +done_testing;
     
    -diff --git a/t/customroles/tickets.t b/t/customroles/tickets.t
    +diff --git a/t/customroles/existing-tickets.t b/t/customroles/existing-tickets.t
     new file mode 100644
     --- /dev/null
    -+++ b/t/customroles/tickets.t
    ++++ b/t/customroles/existing-tickets.t
     @@
     +use strict;
     +use warnings;
     +
     +use RT::Test tests => undef;
     +
    -+my $general = RT::Test->load_or_create_queue( Name => 'General' );
    -+my $inbox = RT::Test->load_or_create_queue( Name => 'Inbox' );
     +my $specs = RT::Test->load_or_create_queue( Name => 'Specs' );
    -+my $development = RT::Test->load_or_create_queue( Name => 'Development' );
     +
     +my $engineer = RT::CustomRole->new(RT->SystemUser);
     +my $sales = RT::CustomRole->new(RT->SystemUser);
    @@ -3234,6 +3319,610 @@
     +my $moss = RT::Test->load_or_create_user( EmailAddress => 'moss at example.com' );
     +my $ricky = RT::Test->load_or_create_user( EmailAddress => 'ricky.roma at example.com' );
     +
    ++ok( RT::Test->add_rights( { Principal => 'Privileged', Right => [ qw(CreateTicket ShowTicket ModifyTicket OwnTicket SeeQueue) ] } ));
    ++
    ++my $t1 = RT::Test->create_ticket(
    ++    Queue   => $specs,
    ++    Subject => 'updates with a first test pass',
    ++);
    ++
    ++my $t2 = RT::Test->create_ticket(
    ++    Queue   => $specs,
    ++    Subject => 'updates without a test pass',
    ++);
    ++
    ++my $sales_grouptype = 'RT::CustomRole-1';
    ++my $engineer_grouptype = 'RT::CustomRole-2';
    ++my $unapplied_grouptype = 'RT::CustomRole-3';
    ++
    ++diag 'try first pass test' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    is($t1->RoleAddresses($engineer_grouptype), '', 'no engineer');
    ++    is($t1->RoleAddresses($sales_grouptype), '', 'no sales');
    ++    is($t1->RoleAddresses($unapplied_grouptype), '', 'no unapplied');
    ++    ok($t1->RoleGroup($engineer_grouptype), 'has a role group object');
    ++    ok(!$t1->RoleGroup($engineer_grouptype)->id, 'has a role group object with no id');
    ++
    ++    my ($ok, $msg) = $t1->AddWatcher(Type => $sales_grouptype, Principal => $ricky->PrincipalObj);
    ++    ok(!$ok, "couldn't add sales: $msg");
    ++    is($t1->RoleAddresses($sales_grouptype), '', 'sales still empty');
    ++
    ++    ($ok, $msg) = $t1->AddWatcher(Type => $engineer_grouptype, Principal => $linus->PrincipalObj);
    ++    ok(!$ok, "couldn't add engineer: $msg");
    ++    is($t1->RoleAddresses($engineer_grouptype), '', 'engineer still empty');
    ++
    ++    ($ok, $msg) = $t1->AddWatcher(Type => $unapplied_grouptype, Principal => $linus->PrincipalObj);
    ++    ok(!$ok, "couldn't add unapplied: $msg");
    ++    is($t1->RoleAddresses($unapplied_grouptype), '', 'no unapplied members');
    ++}
    ++
    ++diag 'create roles and add them to the queue' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $engineer->Create(
    ++        Name      => 'Engineer-' . $$,
    ++        MaxValues => 1,
    ++    );
    ++    ok($ok, "created Engineer role: $msg");
    ++    
    ++    ($ok, $msg) = $sales->Create(
    ++        Name      => 'Sales-' . $$,
    ++        MaxValues => 0,
    ++    );
    ++    ok($ok, "created Sales role: $msg");
    ++    
    ++    ($ok, $msg) = $unapplied->Create(
    ++        Name      => 'Unapplied-' . $$,
    ++        MaxValues => 0,
    ++    );
    ++    ok($ok, "created Unapplied role: $msg");
    ++    
    ++    ($ok, $msg) = $sales->AddToObject($specs->id);
    ++    ok($ok, "added Sales to Specs: $msg");
    ++    
    ++    ($ok, $msg) = $engineer->AddToObject($specs->id);
    ++    ok($ok, "added Engineer to Specs: $msg");
    ++}
    ++
    ++for my $t ($t1, $t2) {
    ++    diag 'test managing watchers of new roles on #' . $t->id if $ENV{'TEST_VERBOSE'};
    ++
    ++    my ($ok, $msg) = $t->AddWatcher(Type => $sales->GroupType, Principal => $ricky->PrincipalObj);
    ++    ok($ok, "add sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), $ricky->EmailAddress, 'sales ricky');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $sales->GroupType, Principal => $moss->PrincipalObj);
    ++    ok($ok, "add sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), (join ', ', sort $ricky->EmailAddress, $moss->EmailAddress), 'sales ricky and moss');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $sales->GroupType, Principal => RT->Nobody->PrincipalObj);
    ++    ok($ok, "add sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), (join ', ', sort $ricky->EmailAddress, $moss->EmailAddress), 'sales ricky and moss');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $engineer->GroupType, Principal => $linus->PrincipalObj);
    ++    ok($ok, "add engineer: $msg");
    ++    is($t->RoleAddresses($engineer->GroupType), $linus->EmailAddress, 'engineer linus');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $engineer->GroupType, Principal => $blake->PrincipalObj);
    ++    ok($ok, "add engineer: $msg");
    ++    is($t->RoleAddresses($engineer->GroupType), $blake->EmailAddress, 'engineer blake (single-member role so linus gets displaced)');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $engineer->GroupType, Principal => RT->Nobody->PrincipalObj);
    ++    ok($ok, "add engineer: $msg");
    ++    is($t->RoleAddresses($engineer->GroupType), '', 'engineer nobody (single-member role so blake gets displaced)');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $unapplied->GroupType, Principal => $linus->PrincipalObj);
    ++    ok(!$ok, "did not add unapplied role member: $msg");
    ++    is($t->RoleAddresses($unapplied->GroupType), '', 'no unapplied members');
    ++
    ++    ok($t->RoleGroup($sales->GroupType), 'has a Sales group object');
    ++    ok($t->RoleGroup($sales->GroupType)->id, 'has a Sales group object with an id');
    ++    ok($t->RoleGroup($engineer->GroupType), 'has an Engineer group object');
    ++    ok($t->RoleGroup($engineer->GroupType)->id, 'has an Engineer group object with an id');
    ++    ok($t->RoleGroup($unapplied->GroupType), 'has an Unapplied group object');
    ++    ok(!$t->RoleGroup($unapplied->GroupType)->id, 'has an Unapplied group object with no id');
    ++}
    ++
    ++done_testing;
    +
    +diff --git a/t/customroles/rights.t b/t/customroles/rights.t
    +new file mode 100644
    +--- /dev/null
    ++++ b/t/customroles/rights.t
    +@@
    ++use strict;
    ++use warnings;
    ++
    ++use RT::Test tests => undef;
    ++
    ++my $general = RT::Test->load_or_create_queue( Name => 'General' );
    ++my $inbox = RT::Test->load_or_create_queue( Name => 'Inbox' );
    ++my $specs = RT::Test->load_or_create_queue( Name => 'Specs' );
    ++my $development = RT::Test->load_or_create_queue( Name => 'Development' );
    ++
    ++my $engineer = RT::CustomRole->new(RT->SystemUser);
    ++my $sales = RT::CustomRole->new(RT->SystemUser);
    ++my $unapplied = RT::CustomRole->new(RT->SystemUser);
    ++
    ++my $linus = RT::Test->load_or_create_user( EmailAddress => 'linus at example.com' );
    ++my $john = RT::Test->load_or_create_user( EmailAddress => 'john at example.com' );
    ++
    ++my $blake = RT::Test->load_or_create_user( EmailAddress => 'blake at example.com' );
    ++my $williamson = RT::Test->load_or_create_user( EmailAddress => 'williamson at example.com' );
    ++my $moss = RT::Test->load_or_create_user( EmailAddress => 'moss at example.com' );
    ++my $ricky = RT::Test->load_or_create_user( EmailAddress => 'ricky.roma at example.com' );
    ++
    ++my $team = RT::Test->load_or_create_group(
    ++    'Team',
    ++    Members => [$blake, $williamson, $moss, $ricky],
    ++);
    ++
    ++diag 'setup' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $engineer->Create(
    ++        Name      => 'Engineer-' . $$,
    ++        MaxValues => 1,
    ++    );
    ++    ok($ok, "created Engineer role: $msg");
    ++
    ++    ($ok, $msg) = $sales->Create(
    ++        Name      => 'Sales-' . $$,
    ++        MaxValues => 0,
    ++    );
    ++    ok($ok, "created Sales role: $msg");
    ++
    ++    ($ok, $msg) = $unapplied->Create(
    ++        Name      => 'Unapplied-' . $$,
    ++        MaxValues => 0,
    ++    );
    ++    ok($ok, "created Unapplied role: $msg");
    ++
    ++    ($ok, $msg) = $sales->AddToObject($inbox->id);
    ++    ok($ok, "added Sales to Inbox: $msg");
    ++
    ++    ($ok, $msg) = $sales->AddToObject($specs->id);
    ++    ok($ok, "added Sales to Specs: $msg");
    ++
    ++    ($ok, $msg) = $engineer->AddToObject($specs->id);
    ++    ok($ok, "added Engineer to Specs: $msg");
    ++
    ++    ($ok, $msg) = $engineer->AddToObject($development->id);
    ++    ok($ok, "added Engineer to Development: $msg");
    ++}
    ++
    ++# the rights are set up as such:
    ++# globally: sales can ShowTicket, engineers can ModifyTicket
    ++# spec queue: sales can CommentOnTicket, engineers can ReplyToTicket
    ++
    ++# blake is set up as sales person on inbox queue but not specs
    ++
    ++diag 'assign rights and queue watcher' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    ok( RT::Test->add_rights( { Principal => $engineer->GroupType, Right => [ qw(ModifyTicket) ] } ));
    ++    ok( RT::Test->add_rights( { Principal => $sales->GroupType, Right => [ qw(ShowTicket) ] } ));
    ++    ok( RT::Test->add_rights( { Principal => $engineer->GroupType, Right => [ qw(ReplyToTicket) ], Object => $specs } ));
    ++    ok( RT::Test->add_rights( { Principal => $sales->GroupType, Right => [ qw(CommentOnTicket) ], Object => $specs } ));
    ++
    ++    ok($inbox->AddWatcher(User => $blake, Type => $sales->GroupType));
    ++}
    ++
    ++my ($inbox_individual, $inbox_group, $specs_individual);
    ++
    ++sub sales_has_rights_for_inbox_individual {
    ++    my $has_right = shift;
    ++    my $rationale = shift || '';
    ++
    ++    my $t = $inbox_individual;
    ++
    ++    if ($has_right) {
    ++        is($t->RoleAddresses($sales->GroupType), (join ', ', sort $moss->EmailAddress, $ricky->EmailAddress), 'got salespeople');
    ++    }
    ++    else {
    ++        is($t->RoleAddresses($sales->GroupType), '', "got no salespeople $rationale");
    ++    }
    ++
    ++    if ($has_right) {
    ++        ok($blake->HasRight(Right => 'ShowTicket', Object => $t), 'blake (queue sales) has right to see the ticket');
    ++        ok($moss->HasRight(Right => 'ShowTicket', Object => $t), 'moss (ticket sales) has right to see the ticket');
    ++        ok($ricky->HasRight(Right => 'ShowTicket', Object => $t), 'ricky (ticket sales) has right to see the ticket');
    ++    }
    ++    else {
    ++        ok(!$blake->HasRight(Right => 'ShowTicket', Object => $t), "blake (queue sales) has no right to see the ticket $rationale");
    ++        ok(!$moss->HasRight(Right => 'ShowTicket', Object => $t), "moss (ticket sales) has no right to see the ticket $rationale");
    ++        ok(!$ricky->HasRight(Right => 'ShowTicket', Object => $t), "ricky (ticket sales) has no right to see the ticket $rationale");
    ++    }
    ++
    ++    ok(!$blake->HasRight(Right => 'ModifyTicket', Object => $t), 'blake has no right to modify the ticket');
    ++    ok(!$blake->HasRight(Right => 'ReplyToTicket', Object => $t), 'blake has no right to reply to the ticket');
    ++    ok(!$blake->HasRight(Right => 'CommentOnTicket', Object => $t), 'blake has no right to comment on the ticket');
    ++    ok(!$moss->HasRight(Right => 'ModifyTicket', Object => $t), 'moss has no right to modify the ticket');
    ++    ok(!$moss->HasRight(Right => 'ReplyToTicket', Object => $t), 'moss has no right to reply to the ticket');
    ++    ok(!$moss->HasRight(Right => 'CommentOnTicket', Object => $t), 'moss has no right to comment on the ticket');
    ++    ok(!$ricky->HasRight(Right => 'ModifyTicket', Object => $t), 'ricky has no right to modify the ticket');
    ++    ok(!$ricky->HasRight(Right => 'ReplyToTicket', Object => $t), 'ricky has no right to reply to the ticket');
    ++    ok(!$ricky->HasRight(Right => 'CommentOnTicket', Object => $t), 'ricky has no right to comment on the ticket');
    ++    ok(!$williamson->HasRight(Right => 'ShowTicket', Object => $t), 'williamson has no right to see the ticket');
    ++    ok(!$williamson->HasRight(Right => 'ModifyTicket', Object => $t), 'williamson has no right to modify the ticket');
    ++    ok(!$williamson->HasRight(Right => 'ReplyToTicket', Object => $t), 'williamson has no right to reply to the ticket');
    ++    ok(!$williamson->HasRight(Right => 'CommentOnTicket', Object => $t), 'williamson has no right to comment on the ticket');
    ++}
    ++
    ++sub engineer_has_no_rights_for_inbox_individual {
    ++    my $user = shift;
    ++    my $t = $inbox_individual;
    ++
    ++    ok(!$user->HasRight(Right => 'ShowTicket', Object => $t), $user->EmailAddress . ' has no right to see the ticket');
    ++    ok(!$user->HasRight(Right => 'ModifyTicket', Object => $t), $user->EmailAddress . ' has no right to modify the ticket');
    ++    ok(!$user->HasRight(Right => 'ReplyToTicket', Object => $t), $user->EmailAddress . ' has no right to reply to the ticket');
    ++    ok(!$user->HasRight(Right => 'CommentOnTicket', Object => $t), $user->EmailAddress . ' has no right to comment on the ticket');
    ++}
    ++
    ++sub sales_has_rights_for_inbox_group {
    ++    my $has_right = shift;
    ++    my $rationale = shift || '';
    ++
    ++    my $t = $inbox_group;
    ++
    ++    if ($has_right) {
    ++        is($t->RoleAddresses($sales->GroupType), (join ', ', sort $moss->EmailAddress, $ricky->EmailAddress, $blake->EmailAddress, $williamson->EmailAddress), 'got all salespeople');
    ++    }
    ++    else {
    ++        is($t->RoleAddresses($sales->GroupType), '', "got no salespeople $rationale");
    ++    }
    ++
    ++    for my $user ($blake, $moss, $ricky, $williamson) {
    ++        if ($has_right) {
    ++            ok($user->HasRight(Right => 'ShowTicket', Object => $t), $user->Name . " (member of ticket sales group team) has right to see the ticket");
    ++        }
    ++        else {
    ++            ok(!$user->HasRight(Right => 'ShowTicket', Object => $t), $user->Name . " (member of ticket sales group team) has no right to see the ticket $rationale");
    ++        }
    ++
    ++        ok(!$user->HasRight(Right => 'ModifyTicket', Object => $t), $user->Name . " (member of ticket sales group team) has no right to modify the ticket");
    ++        ok(!$user->HasRight(Right => 'ReplyToTicket', Object => $t), $user->Name . " (member of ticket sales group team) has no right to reply to the ticket");
    ++        ok(!$user->HasRight(Right => 'CommentOnTicket', Object => $t), $user->Name . " (member of ticket sales group team) has no right to comment on the ticket");
    ++    }
    ++
    ++    ok(!$linus->HasRight(Right => 'ShowTicket', Object => $t), "linus has no ShowTicket on inbox");
    ++    ok(!$linus->HasRight(Right => 'ModifyTicket', Object => $t), "linus has no ModifyTicket on inbox");
    ++    ok(!$linus->HasRight(Right => 'ReplyToTicket', Object => $t), "linus has no ReplyToTicket on inbox");
    ++    ok(!$linus->HasRight(Right => 'CommentOnTicket', Object => $t), "linus has no CommentOnTicket on inbox");
    ++}
    ++
    ++sub sales_has_rights_for_specs_individual {
    ++    my $has_right = shift;
    ++    my $rationale = shift || '';
    ++
    ++    my $t = $specs_individual;
    ++
    ++    if (!$has_right || $has_right == 2) {
    ++        is($t->RoleAddresses($sales->GroupType), '', "got no salespeople $rationale");
    ++    }
    ++    else {
    ++        is($t->RoleAddresses($sales->GroupType), (join ', ', sort $moss->EmailAddress, $ricky->EmailAddress), 'got salespeople');
    ++    }
    ++
    ++    if (!$has_right) {
    ++        ok(!$moss->HasRight(Right => 'ShowTicket', Object => $t), "moss (ticket sales) has no right to see the ticket $rationale");
    ++        ok(!$moss->HasRight(Right => 'CommentOnTicket', Object => $t), "moss (ticket sales) has no right to comment on the ticket $rationale");
    ++        ok(!$ricky->HasRight(Right => 'ShowTicket', Object => $t), "ricky (ticket sales) has no right to see the ticket $rationale");
    ++        ok(!$ricky->HasRight(Right => 'CommentOnTicket', Object => $t), "ricky (ticket sales) has no right to comment on the ticket $rationale");
    ++    }
    ++    elsif ($has_right == 2) {
    ++        ok($moss->HasRight(Right => 'ShowTicket', Object => $t), 'moss (ticket sales) has right to see the ticket thru global sales right');
    ++        ok(!$moss->HasRight(Right => 'CommentOnTicket', Object => $t), "moss (ticket sales) has no right to comment on the ticket $rationale");
    ++        ok($ricky->HasRight(Right => 'ShowTicket', Object => $t), 'ricky (ticket sales) has right to see the ticket thru global sales right');
    ++        ok(!$ricky->HasRight(Right => 'CommentOnTicket', Object => $t), "ricky (ticket sales) has no right to comment on the ticket $rationale");
    ++    }
    ++    else {
    ++        ok($moss->HasRight(Right => 'ShowTicket', Object => $t), 'moss (ticket sales) has right to see the ticket');
    ++        ok($moss->HasRight(Right => 'CommentOnTicket', Object => $t), 'moss (ticket sales) has right to comment on the ticket');
    ++        ok($ricky->HasRight(Right => 'ShowTicket', Object => $t), 'ricky (ticket sales) has right to see the ticket');
    ++        ok($ricky->HasRight(Right => 'CommentOnTicket', Object => $t), 'ricky (ticket sales) has right to comment on the ticket');
    ++    }
    ++
    ++    ok(!$blake->HasRight(Right => 'ShowTicket', Object => $t), 'blake has no right to see the ticket');
    ++    ok(!$blake->HasRight(Right => 'ModifyTicket', Object => $t), 'blake has no right to modify the ticket');
    ++    ok(!$blake->HasRight(Right => 'ReplyToTicket', Object => $t), 'blake has no right to reply to the ticket');
    ++    ok(!$blake->HasRight(Right => 'CommentOnTicket', Object => $t), 'blake has no right to comment on the ticket');
    ++    ok(!$moss->HasRight(Right => 'ModifyTicket', Object => $t), 'moss has no right to modify the ticket');
    ++    ok(!$moss->HasRight(Right => 'ReplyToTicket', Object => $t), 'moss has no right to reply to the ticket');
    ++    ok(!$ricky->HasRight(Right => 'ModifyTicket', Object => $t), 'ricky has no right to modify the ticket');
    ++    ok(!$ricky->HasRight(Right => 'ReplyToTicket', Object => $t), 'ricky has no right to reply to the ticket');
    ++    ok(!$williamson->HasRight(Right => 'ShowTicket', Object => $t), 'williamson has no right to see the ticket');
    ++    ok(!$williamson->HasRight(Right => 'ModifyTicket', Object => $t), 'williamson has no right to modify the ticket');
    ++    ok(!$williamson->HasRight(Right => 'ReplyToTicket', Object => $t), 'williamson has no right to reply to the ticket');
    ++    ok(!$williamson->HasRight(Right => 'CommentOnTicket', Object => $t), 'williamson has no right to comment on the ticket');
    ++}
    ++
    ++sub engineer_has_rights_for_specs_individual {
    ++    my $user = shift;
    ++    my $has_right = shift;
    ++    my $t = $specs_individual;
    ++
    ++    ok(!$user->HasRight(Right => 'ShowTicket', Object => $t), $user->EmailAddress . ' has no right to see the ticket');
    ++    ok(!$user->HasRight(Right => 'CommentOnTicket', Object => $t), $user->EmailAddress . ' has no right to comment on the ticket');
    ++
    ++    if ($has_right) {
    ++        ok($user->HasRight(Right => 'ModifyTicket', Object => $t), $user->EmailAddress . ' (ticket engineer) has right to modify the ticket');
    ++        ok($user->HasRight(Right => 'ReplyToTicket', Object => $t), $user->EmailAddress . ' (ticket engineer) has right to reply to the ticket');
    ++    }
    ++}
    ++
    ++diag 'check individual rights on Inbox' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my $t = $inbox_individual = RT::Test->create_ticket(
    ++        Queue => $inbox,
    ++        Subject => 'wrongs',
    ++        $sales->GroupType => [$moss->EmailAddress, $ricky->EmailAddress],
    ++    );
    ++    ok($t->id, 'created ticket');
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++}
    ++
    ++diag 'check group rights on Inbox' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my $t = $inbox_group = RT::Test->create_ticket(
    ++        Queue => $inbox,
    ++        Subject => 'wrongs',
    ++        $sales->GroupType => $team->PrincipalId,
    ++    );
    ++    ok($t->id, 'created ticket');
    ++
    ++    sales_has_rights_for_inbox_group(1);
    ++}
    ++
    ++diag 'check individual rights on Specs' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my $t = $specs_individual = RT::Test->create_ticket(
    ++        Queue => $specs,
    ++        Subject => 'wrongs',
    ++        $engineer->GroupType => $linus->PrincipalId,
    ++        $sales->GroupType => [$moss->EmailAddress, $ricky->EmailAddress],
    ++    );
    ++    ok($t->id, 'created ticket');
    ++
    ++    sales_has_rights_for_specs_individual(1);
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'disable Sales custom role to see how it shakes out permissions' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->SetDisabled(1);
    ++    ok($ok, $msg);
    ++
    ++    sales_has_rights_for_inbox_individual(0, 'because sales role is disabled');
    ++    sales_has_rights_for_inbox_group(0, 'because sales role is disabled');
    ++    sales_has_rights_for_specs_individual(0, 'because sales role is disabled');
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 're-enable Sales custom role to make sure all old group rights and memberships come back' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->SetDisabled(0);
    ++    ok($ok, $msg);
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'remove Sales custom role from Inbox queue' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->RemoveFromObject($inbox->id);
    ++    ok($ok, "removed Sales from Inbox: $msg");
    ++
    ++    sales_has_rights_for_inbox_individual(0, 'because sales role was removed from Inbox');
    ++    sales_has_rights_for_inbox_group(0, 'because sales role was removed from Inbox');
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 're-add Sales custom role to Inbox queue' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->AddToObject($inbox->id);
    ++    ok($ok, "re-added Sales to Specs: $msg");
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'remove Sales custom role from Inbox queue...' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->RemoveFromObject($inbox->id);
    ++    ok($ok, "removed Sales from Inbox: $msg");
    ++
    ++    sales_has_rights_for_inbox_individual(0, 'because sales role was removed from Inbox');
    ++    sales_has_rights_for_inbox_group(0, 'because sales role was removed from Inbox');
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'disable Sales custom role to see how it shakes out permissions' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->SetDisabled(1);
    ++    ok($ok, $msg);
    ++
    ++    sales_has_rights_for_inbox_individual(0, 'because sales role is disabled and was removed from Inbox');
    ++    sales_has_rights_for_inbox_group(0, 'because sales role is disabled and was removed from Inbox');
    ++    sales_has_rights_for_specs_individual(0, 'because sales role is disabled');
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 're-enable Sales custom role to make sure specs regains rights and members but inbox does not because it was removed' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->SetDisabled(0);
    ++    ok($ok, $msg);
    ++
    ++    sales_has_rights_for_inbox_individual(0, 'because sales role is still removed from Inbox');
    ++    sales_has_rights_for_inbox_group(0, 'because sales role is still removed from Inbox');
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 're-add Sales custom role to Inbox queue' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $sales->AddToObject($inbox->id);
    ++    ok($ok, "re-added Sales to Specs: $msg");
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'change engineer from linus to john' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $specs_individual->AddWatcher(Type => $engineer->GroupType, Principal => $john->PrincipalObj);
    ++    ok($ok, "set John as engineer: $msg");
    ++    is($specs_individual->RoleAddresses($engineer->GroupType), $john->EmailAddress, 'engineer set to John');
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 0);
    ++    engineer_has_rights_for_specs_individual($john => 1);
    ++}
    ++
    ++diag 'change engineer from john to nobody' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $specs_individual->AddWatcher(Type => $engineer->GroupType, Principal => RT->Nobody->PrincipalObj);
    ++    ok($ok, "set Nobody as engineer: $msg");
    ++    is($specs_individual->RoleAddresses($engineer->GroupType), '', 'engineer set to Nobody');
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 0);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'change engineer from nobody to linus' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $specs_individual->AddWatcher(Type => $engineer->GroupType, Principal => $linus->PrincipalObj);
    ++    ok($ok, "set Linus as engineer: $msg");
    ++    is($specs_individual->RoleAddresses($engineer->GroupType), $linus->EmailAddress, 'engineer set to Linus');
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'change queue from Specs to General' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $specs_individual->SetQueue($general->Id);
    ++    ok($ok, "set queue to General: $msg");
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(2, 'queue changed to General');
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 0);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++diag 'change queue from General to Specs' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my ($ok, $msg) = $specs_individual->SetQueue($specs->Id);
    ++    ok($ok, "set queue to Specs: $msg");
    ++
    ++    sales_has_rights_for_inbox_individual(1);
    ++    sales_has_rights_for_inbox_group(1);
    ++    sales_has_rights_for_specs_individual(1);
    ++
    ++    engineer_has_no_rights_for_inbox_individual($_) for $linus, $john;
    ++    engineer_has_rights_for_specs_individual($linus => 1);
    ++    engineer_has_rights_for_specs_individual($john => 0);
    ++}
    ++
    ++done_testing;
    ++
    +
    +diff --git a/t/customroles/tickets.t b/t/customroles/tickets.t
    +new file mode 100644
    +--- /dev/null
    ++++ b/t/customroles/tickets.t
    +@@
    ++use strict;
    ++use warnings;
    ++
    ++use RT::Test tests => undef;
    ++
    ++my $general = RT::Test->load_or_create_queue( Name => 'General' );
    ++my $inbox = RT::Test->load_or_create_queue( Name => 'Inbox' );
    ++my $specs = RT::Test->load_or_create_queue( Name => 'Specs' );
    ++my $development = RT::Test->load_or_create_queue( Name => 'Development' );
    ++
    ++my $engineer = RT::CustomRole->new(RT->SystemUser);
    ++my $sales = RT::CustomRole->new(RT->SystemUser);
    ++my $unapplied = RT::CustomRole->new(RT->SystemUser);
    ++
    ++my $linus = RT::Test->load_or_create_user( EmailAddress => 'linus at example.com' );
    ++my $blake = RT::Test->load_or_create_user( EmailAddress => 'blake at example.com' );
    ++my $williamson = RT::Test->load_or_create_user( EmailAddress => 'williamson at example.com' );
    ++my $moss = RT::Test->load_or_create_user( EmailAddress => 'moss at example.com' );
    ++my $ricky = RT::Test->load_or_create_user( EmailAddress => 'ricky.roma at example.com' );
    ++
    ++my $team = RT::Test->load_or_create_group(
    ++    'Team',
    ++    Members => [$blake, $williamson, $moss, $ricky],
    ++);
    ++
    ++sub txn_messages_like {
    ++    local $Test::Builder::Level = $Test::Builder::Level + 1;
    ++
    ++    my $t = shift;
    ++    my $re = shift;
    ++
    ++    my $txns = $t->Transactions;
    ++    $txns->Limit(FIELD => 'Type', VALUE => 'SetWatcher');
    ++    $txns->Limit(FIELD => 'Type', VALUE => 'AddWatcher');
    ++    $txns->Limit(FIELD => 'Type', VALUE => 'DelWatcher');
    ++
    ++    is($txns->Count, scalar(@$re), 'expected number of transactions');
    ++
    ++    while (my $txn = $txns->Next) {
    ++        like($txn->BriefDescription, (shift(@$re) || qr/(?!)/));
    ++    }
    ++}
     +
     +diag 'setup' if $ENV{'TEST_VERBOSE'};
     +{
    @@ -3294,7 +3983,7 @@
     +        AdminCc   => [$blake->EmailAddress],
     +    );
     +    is($general2->OwnerObj->id, $linus->id, 'owner is correct');
    -+    is($general2->RequestorAddresses, (join ', ', $moss->EmailAddress, $williamson->EmailAddress), 'requestors correct');
    ++    is($general2->RequestorAddresses, (join ', ', sort $moss->EmailAddress, $williamson->EmailAddress), 'requestors correct');
     +    is($general2->CcAddresses, $ricky->EmailAddress, 'cc correct');
     +    is($general2->AdminCcAddresses, $blake->EmailAddress, 'admincc correct');
     +    is($general2->RoleAddresses($engineer->GroupType), '', 'no engineer (role not applied to queue)');
    @@ -3339,7 +4028,7 @@
     +        AdminCc   => [$blake->EmailAddress],
     +    );
     +    is($inbox2->OwnerObj->id, $linus->id, 'owner is correct');
    -+    is($inbox2->RequestorAddresses, (join ', ', $moss->EmailAddress, $williamson->EmailAddress), 'requestors correct');
    ++    is($inbox2->RequestorAddresses, (join ', ', sort $moss->EmailAddress, $williamson->EmailAddress), 'requestors correct');
     +    is($inbox2->CcAddresses, $ricky->EmailAddress, 'cc correct');
     +    is($inbox2->AdminCcAddresses, $blake->EmailAddress, 'admincc correct');
     +    is($inbox2->RoleAddresses($engineer->GroupType), '', 'no engineer (role not applied to queue)');
    @@ -3358,8 +4047,185 @@
     +    is($inbox3->AdminCcAddresses, '', 'no admincc');
     +    is($inbox3->RoleAddresses($engineer->GroupType), '', 'no engineer (role not applied to queue)');
     +    is($inbox3->RoleAddresses($sales->GroupType), $blake->EmailAddress, 'got sales');
    ++
    ++    my $inbox4 = RT::Test->create_ticket(
    ++        Queue                => $inbox,
    ++        Subject              => 'more',
    ++        Owner                => $ricky,
    ++        $engineer->GroupType => $linus,
    ++        $sales->GroupType    => [$blake->EmailAddress, $williamson->EmailAddress],
    ++    );
    ++    is($inbox4->OwnerObj->id, $ricky->id, 'owner is correct');
    ++    is($inbox4->RequestorAddresses, '', 'no requestors');
    ++    is($inbox4->CcAddresses, '', 'no cc');
    ++    is($inbox4->AdminCcAddresses, '', 'no admincc');
    ++    is($inbox4->RoleAddresses($engineer->GroupType), '', 'no engineer (role not applied to queue)');
    ++    is($inbox4->RoleAddresses($sales->GroupType), (join ', ', sort $blake->EmailAddress, $williamson->EmailAddress), 'got sales');
    ++}
    ++
    ++diag 'create tickets in Specs (both roles)' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my $specs1 = RT::Test->create_ticket(
    ++        Queue     => $specs,
    ++        Subject   => 'a ticket',
    ++        Owner     => $williamson,
    ++        Requestor => [$blake->EmailAddress],
    ++    );
    ++    is($specs1->OwnerObj->id, $williamson->id, 'owner is correct');
    ++    is($specs1->RequestorAddresses, $blake->EmailAddress, 'requestors correct');
    ++    is($specs1->CcAddresses, '', 'no ccs');
    ++    is($specs1->AdminCcAddresses, '', 'no adminccs');
    ++    is($specs1->RoleAddresses($engineer->GroupType), '', 'no engineer (role not applied to queue)');
    ++    is($specs1->RoleAddresses($sales->GroupType), '', 'no sales (role not applied to queue)');
    ++
    ++    my $specs2 = RT::Test->create_ticket(
    ++        Queue     => $specs,
    ++        Subject   => 'another ticket',
    ++        Owner     => $linus,
    ++        Requestor => [$moss->EmailAddress, $williamson->EmailAddress],
    ++        Cc        => [$ricky->EmailAddress],
    ++        AdminCc   => [$blake->EmailAddress],
    ++    );
    ++    is($specs2->OwnerObj->id, $linus->id, 'owner is correct');
    ++    is($specs2->RequestorAddresses, (join ', ', sort $moss->EmailAddress, $williamson->EmailAddress), 'requestors correct');
    ++    is($specs2->CcAddresses, $ricky->EmailAddress, 'cc correct');
    ++    is($specs2->AdminCcAddresses, $blake->EmailAddress, 'admincc correct');
    ++    is($specs2->RoleAddresses($engineer->GroupType), '', 'no engineer (role not applied to queue)');
    ++    is($specs2->RoleAddresses($sales->GroupType), '', 'no sales (role not applied to queue)');
    ++
    ++    my $specs3 = RT::Test->create_ticket(
    ++        Queue                => $specs,
    ++        Subject              => 'oops',
    ++        Owner                => $ricky,
    ++        $engineer->GroupType => $linus,
    ++        $sales->GroupType    => [$blake->EmailAddress],
    ++    );
    ++    is($specs3->OwnerObj->id, $ricky->id, 'owner is correct');
    ++    is($specs3->RequestorAddresses, '', 'no requestors');
    ++    is($specs3->CcAddresses, '', 'no cc');
    ++    is($specs3->AdminCcAddresses, '', 'no admincc');
    ++    is($specs3->RoleAddresses($engineer->GroupType), $linus->EmailAddress, 'got engineer');
    ++    is($specs3->RoleAddresses($sales->GroupType), $blake->EmailAddress, 'got sales');
    ++
    ++    my $specs4 = RT::Test->create_ticket(
    ++        Queue                => $specs,
    ++        Subject              => 'more',
    ++        Owner                => $ricky,
    ++        $engineer->GroupType => $linus,
    ++        $sales->GroupType    => [$blake->EmailAddress, $williamson->EmailAddress],
    ++    );
    ++    is($specs4->OwnerObj->id, $ricky->id, 'owner is correct');
    ++    is($specs4->RequestorAddresses, '', 'no requestors');
    ++    is($specs4->CcAddresses, '', 'no cc');
    ++    is($specs4->AdminCcAddresses, '', 'no admincc');
    ++    is($specs4->RoleAddresses($engineer->GroupType), $linus->EmailAddress, 'got engineer');
    ++    is($specs4->RoleAddresses($sales->GroupType), (join ', ', sort $blake->EmailAddress, $williamson->EmailAddress), 'got sales');
    ++}
    ++
    ++diag 'update ticket in Specs' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my $t = RT::Test->create_ticket(
    ++        Queue   => $specs,
    ++        Subject => 'updates',
    ++    );
    ++
    ++    is($t->OwnerObj->id, RT->Nobody->id, 'owner nobody');
    ++    is($t->RequestorAddresses, '', 'no requestors');
    ++    is($t->CcAddresses, '', 'no cc');
    ++    is($t->AdminCcAddresses, '', 'no admincc');
    ++    is($t->RoleAddresses($engineer->GroupType), '', 'no engineer');
    ++    is($t->RoleAddresses($sales->GroupType), '', 'no sales');
    ++    is($t->RoleAddresses($unapplied->GroupType), '', 'no unapplied');
    ++
    ++    my ($ok, $msg) = $t->SetOwner($linus);
    ++    ok($ok, "set owner: $msg");
    ++    is($t->OwnerObj->id, $linus->id, 'owner linus');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => 'Requestor', Principal => $ricky->PrincipalObj);
    ++    ok($ok, "add requestor: $msg");
    ++    is($t->RequestorAddresses, $ricky->EmailAddress, 'requestor ricky');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => 'AdminCc', Principal => $blake->PrincipalObj);
    ++    ok($ok, "add admincc: $msg");
    ++    is($t->AdminCcAddresses, $blake->EmailAddress, 'admincc blake');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => 'Cc', Principal => $moss->PrincipalObj);
    ++    ok($ok, "add cc: $msg");
    ++    is($t->CcAddresses, $moss->EmailAddress, 'cc moss');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $sales->GroupType, Principal => $ricky->PrincipalObj);
    ++    ok($ok, "add sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), $ricky->EmailAddress, 'sales ricky');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $sales->GroupType, Principal => $moss->PrincipalObj);
    ++    ok($ok, "add sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), (join ', ', sort $ricky->EmailAddress, $moss->EmailAddress), 'sales ricky and moss');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $sales->GroupType, Principal => RT->Nobody->PrincipalObj);
    ++    ok($ok, "add sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), (join ', ', sort $ricky->EmailAddress, $moss->EmailAddress), 'sales ricky and moss');
    ++
    ++    ($ok, $msg) = $t->DeleteWatcher(Type => $sales->GroupType, PrincipalId => $moss->PrincipalId);
    ++    ok($ok, "remove sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), $ricky->EmailAddress, 'sales ricky');
    ++
    ++    ($ok, $msg) = $t->DeleteWatcher(Type => $sales->GroupType, PrincipalId => $ricky->PrincipalId);
    ++    ok($ok, "remove sales: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), '', 'sales empty');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $engineer->GroupType, Principal => $linus->PrincipalObj);
    ++    ok($ok, "add engineer: $msg");
    ++    is($t->RoleAddresses($engineer->GroupType), $linus->EmailAddress, 'engineer linus');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $engineer->GroupType, Principal => $blake->PrincipalObj);
    ++    ok($ok, "add engineer: $msg");
    ++    is($t->RoleAddresses($engineer->GroupType), $blake->EmailAddress, 'engineer blake (single-member role so linus gets displaced)');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $engineer->GroupType, Principal => RT->Nobody->PrincipalObj);
    ++    ok($ok, "add engineer: $msg");
    ++    is($t->RoleAddresses($engineer->GroupType), '', 'engineer nobody (single-member role so blake gets displaced)');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $unapplied->GroupType, Principal => $linus->PrincipalObj);
    ++    ok(!$ok, "did not add unapplied role member: $msg");
    ++    like($msg, qr/That role is invalid for this object/);
    ++    is($t->RoleAddresses($unapplied->GroupType), '', 'no unapplied members');
    ++
    ++    txn_messages_like($t, [
    ++        qr/Owner set to linus\@example\.com/,
    ++        qr/Requestor ricky\.roma\@example\.com added/,
    ++        qr/AdminCc blake\@example\.com added/,
    ++        qr/Cc moss\@example\.com added/,
    ++        qr/Sales-$$ ricky\.roma\@example\.com added/,
    ++        qr/Sales-$$ moss\@example\.com added/,
    ++        qr/Sales-$$ Nobody in particular added/,
    ++        qr/Sales-$$ moss\@example\.com deleted/,
    ++        qr/Sales-$$ ricky\.roma\@example\.com deleted/,
    ++        qr/Engineer-$$ set to linus\@example\.com/,
    ++        qr/Engineer-$$ set to blake\@example\.com/,
    ++        qr/Engineer-$$ set to Nobody in particular/,
    ++    ]);
    ++}
    ++
    ++diag 'groups can be role members' if $ENV{'TEST_VERBOSE'};
    ++{
    ++    my $t = RT::Test->create_ticket(
    ++        Queue   => $specs,
    ++        Subject => 'groups',
    ++    );
    ++
    ++    my ($ok, $msg) = $t->AddWatcher(Type => $sales->GroupType, Principal => $team->PrincipalObj);
    ++    ok($ok, "add team: $msg");
    ++    is($t->RoleAddresses($sales->GroupType), (join ', ', sort $blake->EmailAddress, $ricky->EmailAddress, $moss->EmailAddress, $williamson->EmailAddress), 'sales is all the team members');
    ++
    ++    ($ok, $msg) = $t->AddWatcher(Type => $engineer->GroupType, Principal => $team->PrincipalObj);
    ++    ok(!$ok, "could not add team: $msg");
    ++    like($msg, qr/cannot be a group/);
    ++    is($t->RoleAddresses($engineer->GroupType), '', 'engineer is still nobody');
    ++
    ++    txn_messages_like($t, [
    ++        qr/Sales-$$ group Team added/,
    ++    ]);
     +}
     +
     +done_testing;
    -+
     
29:  5a924ad = 29:  b35a81d @CustomRoles in initialdata, support CustomRole in @ACL
30:  8be477a = 30:  bb1cc0d Add custom roles to search builder
31:  1a8e3a1 = 31:  6c22fa9 Ensure custom role name uniqueness in rt-validator



More information about the rt-commit mailing list