1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
6 # <jesse@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 # CONTRIBUTION SUBMISSION POLICY:
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
45 # END BPS TAGGED BLOCK }}}
49 RT::Queue - an RT Queue object
72 no warnings qw(redefine);
74 use vars qw(@DEFAULT_ACTIVE_STATUS @DEFAULT_INACTIVE_STATUS $RIGHTS);
78 use RT::Interface::Email;
80 @DEFAULT_ACTIVE_STATUS = qw(new open stalled);
81 @DEFAULT_INACTIVE_STATUS = qw(resolved rejected deleted);
83 # $self->loc('new'); # For the string extractor to get a string to localize
84 # $self->loc('open'); # For the string extractor to get a string to localize
85 # $self->loc('stalled'); # For the string extractor to get a string to localize
86 # $self->loc('resolved'); # For the string extractor to get a string to localize
87 # $self->loc('rejected'); # For the string extractor to get a string to localize
88 # $self->loc('deleted'); # For the string extractor to get a string to localize
92 SeeQueue => 'Can this principal see this queue', # loc_pair
93 AdminQueue => 'Create, delete and modify queues', # loc_pair
94 ShowACL => 'Display Access Control List', # loc_pair
95 ModifyACL => 'Modify Access Control List', # loc_pair
96 ModifyQueueWatchers => 'Modify the queue watchers', # loc_pair
97 AssignCustomFields => 'Assign and remove custom fields', # loc_pair
98 ModifyTemplate => 'Modify Scrip templates for this queue', # loc_pair
99 ShowTemplate => 'Display Scrip templates for this queue', # loc_pair
101 ModifyScrips => 'Modify Scrips for this queue', # loc_pair
102 ShowScrips => 'Display Scrips for this queue', # loc_pair
104 ShowTicket => 'See ticket summaries', # loc_pair
105 ShowTicketComments => 'See ticket private commentary', # loc_pair
106 ShowOutgoingEmail => 'See exact outgoing email messages and their recipeients', # loc_pair
108 Watch => 'Sign up as a ticket Requestor or ticket or queue Cc', # loc_pair
109 WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc', # loc_pair
110 CreateTicket => 'Create tickets in this queue', # loc_pair
111 ReplyToTicket => 'Reply to tickets', # loc_pair
112 CommentOnTicket => 'Comment on tickets', # loc_pair
113 OwnTicket => 'Own tickets', # loc_pair
114 ModifyTicket => 'Modify tickets', # loc_pair
115 DeleteTicket => 'Delete tickets', # loc_pair
116 TakeTicket => 'Take tickets', # loc_pair
117 StealTicket => 'Steal tickets', # loc_pair
121 # Tell RT::ACE that this sort of object can get acls granted
122 $RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
124 # TODO: This should be refactored out into an RT::ACLedObject or something
125 # stuff the rights into a hash of rights that can exist.
127 foreach my $right ( keys %{$RIGHTS} ) {
128 $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
134 my %args = ( Target => '',
140 unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
141 return ( 0, $self->loc("Permission Denied") );
144 return $self->SUPER::_AddLink(%args);
157 unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
158 $RT::Logger->debug("No permission to delete links\n");
159 return ( 0, $self->loc('Permission Denied'))
162 return $self->SUPER::_DeleteLink(%args);
165 =head2 AvailableRights
167 Returns a hash of available rights for this object. The keys are the right names and the values are a description of what the rights do
171 sub AvailableRights {
176 # {{{ ActiveStatusArray
178 =head2 ActiveStatusArray
180 Returns an array of all ActiveStatuses for this queue
184 sub ActiveStatusArray {
186 if (@RT::ActiveStatus) {
187 return (@RT::ActiveStatus)
189 $RT::Logger->warning("RT::ActiveStatus undefined, falling back to deprecated defaults");
190 return (@DEFAULT_ACTIVE_STATUS);
196 # {{{ InactiveStatusArray
198 =head2 InactiveStatusArray
200 Returns an array of all InactiveStatuses for this queue
204 sub InactiveStatusArray {
206 if (@RT::InactiveStatus) {
207 return (@RT::InactiveStatus)
209 $RT::Logger->warning("RT::InactiveStatus undefined, falling back to deprecated defaults");
210 return (@DEFAULT_INACTIVE_STATUS);
220 Returns an array of all statuses for this queue
226 return ($self->ActiveStatusArray(), $self->InactiveStatusArray());
233 =head2 IsValidStatus VALUE
235 Returns true if VALUE is a valid status. Otherwise, returns 0.
239 my $q = RT::Queue->new($RT::SystemUser);
240 ok($q->IsValidStatus('new')== 1, 'New is a valid status');
241 ok($q->IsValidStatus('f00')== 0, 'f00 is not a valid status');
251 my $retval = grep ( $_ eq $value, $self->StatusArray );
260 =head2 IsActiveStatus VALUE
262 Returns true if VALUE is a Active status. Otherwise, returns 0
266 my $q = RT::Queue->new($RT::SystemUser);
267 ok($q->IsActiveStatus('new')== 1, 'New is a Active status');
268 ok($q->IsActiveStatus('rejected')== 0, 'Rejected is an inactive status');
269 ok($q->IsActiveStatus('f00')== 0, 'f00 is not a Active status');
279 my $retval = grep ( $_ eq $value, $self->ActiveStatusArray );
286 # {{{ IsInactiveStatus
288 =head2 IsInactiveStatus VALUE
290 Returns true if VALUE is a Inactive status. Otherwise, returns 0
294 my $q = RT::Queue->new($RT::SystemUser);
295 ok($q->IsInactiveStatus('new')== 0, 'New is a Active status');
296 ok($q->IsInactiveStatus('rejected')== 1, 'rejeected is an Inactive status');
297 ok($q->IsInactiveStatus('f00')== 0, 'f00 is not a Active status');
303 sub IsInactiveStatus {
307 my $retval = grep ( $_ eq $value, $self->InactiveStatusArray );
322 Arguments: ARGS is a hash of named parameters. Valid parameters are:
332 If you pass the ACL check, it creates the queue and returns its queue id.
336 my $queue = RT::Queue->new($RT::SystemUser);
337 my ($id, $val) = $queue->Create( Name => 'Test1');
340 ($id, $val) = $queue->Create( Name => '66');
351 CorrespondAddress => '',
353 CommentAddress => '',
354 InitialPriority => "0",
355 FinalPriority => "0",
360 unless ( $self->CurrentUser->HasRight(Right => 'AdminQueue', Object => $RT::System) )
362 return ( 0, $self->loc("No permission to create queues") );
365 unless ( $self->ValidateName( $args{'Name'} ) ) {
366 return ( 0, $self->loc('Queue already exists') );
369 #TODO better input validation
370 $RT::Handle->BeginTransaction();
372 my $id = $self->SUPER::Create(%args);
374 $RT::Handle->Rollback();
375 return ( 0, $self->loc('Queue could not be created') );
378 my $create_ret = $self->_CreateQueueGroups();
379 unless ($create_ret) {
380 $RT::Handle->Rollback();
381 return ( 0, $self->loc('Queue could not be created') );
384 $RT::Handle->Commit();
385 return ( $id, $self->loc("Queue created") );
395 $self->loc('Deleting this object would break referential integrity') );
400 # {{{ sub SetDisabled
405 1 will cause this queue to no longer be available for tickets.
406 0 will re-enable this queue.
416 Takes either a numerical id or a textual Name and loads the specified queue.
423 my $identifier = shift;
424 if ( !$identifier ) {
428 if ( $identifier =~ /^(\d+)$/ ) {
429 $self->SUPER::LoadById($identifier);
432 $self->LoadByCols( Name => $identifier );
435 return ( $self->Id );
441 # {{{ sub ValidateName
443 =head2 ValidateName NAME
445 Takes a queue name. Returns true if it's an ok name for
446 a new queue. Returns undef if there's already a queue by that name.
454 my $tempqueue = new RT::Queue($RT::SystemUser);
455 $tempqueue->Load($name);
457 #If this queue exists, return undef
458 if ( $tempqueue->Name() && $tempqueue->id != $self->id) {
462 #If the queue doesn't exist, return 1
464 return ($self->SUPER::ValidateName($name));
475 Returns an RT::Templates object of all of this queue's templates.
482 my $templates = RT::Templates->new( $self->CurrentUser );
484 if ( $self->CurrentUserHasRight('ShowTemplate') ) {
485 $templates->LimitToQueue( $self->id );
493 # {{{ Dealing with custom fields
497 =head2 CustomField NAME
499 Load the queue-specific custom field named NAME
506 my $cf = RT::CustomField->new($self->CurrentUser);
507 $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id);
516 Returns an RT::CustomFields object containing all global custom fields, as well as those tied to this queue
520 # XXX TODO - this should become TicketCustomFields
524 warn "Queue->CustomFields is deprecated, use Queue->TicketCustomFields instead at (". join(":",caller).")";
525 return $self->TicketCustomFields(@_);
528 sub TicketCustomFields {
531 my $cfs = RT::CustomFields->new( $self->CurrentUser );
532 if ( $self->CurrentUserHasRight('SeeQueue') ) {
533 $cfs->LimitToGlobalOrObjectId( $self->Id );
534 $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket' );
539 sub TicketTransactionCustomFields {
542 my $cfs = RT::CustomFields->new( $self->CurrentUser );
543 if ( $self->CurrentUserHasRight('SeeQueue') ) {
544 $cfs->LimitToGlobalOrObjectId( $self->Id );
545 $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' );
555 # {{{ Routines dealing with watchers.
557 # {{{ _CreateQueueGroups
559 =head2 _CreateQueueGroups
561 Create the ticket groups and links for this ticket.
562 This routine expects to be called from Ticket->Create _inside of a transaction_
564 It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
566 It will return true on success and undef on failure.
570 my $Queue = RT::Queue->new($RT::SystemUser); my ($id, $msg) = $Queue->Create(Name => "Foo",
572 ok ($id, "Foo $id was created");
573 ok(my $group = RT::Group->new($RT::SystemUser));
574 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
575 ok ($group->Id, "Found the requestors object for this Queue");
578 ok ((my $add_id, $add_msg) = $Queue->AddWatcher(Type => 'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
579 ok ($add_id, "Add succeeded: ($add_msg)");
580 ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user");
581 $bob->LoadByEmail('bob@fsck.com');
582 ok($bob->Id, "Found the bob rt user");
583 ok ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $bob->PrincipalId), "The Queue actually has bob at fsck.com as a requestor");;
584 ok ((my $add_id, $add_msg) = $Queue->DeleteWatcher(Type =>'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
585 ok (!$Queue->IsWatcher(Type => 'Cc', Principal => $bob->PrincipalId), "The Queue no longer has bob at fsck.com as a requestor");;
588 $group = RT::Group->new($RT::SystemUser);
589 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
590 ok ($group->Id, "Found the cc object for this Queue");
591 $group = RT::Group->new($RT::SystemUser);
592 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'AdminCc'));
593 ok ($group->Id, "Found the AdminCc object for this Queue");
600 sub _CreateQueueGroups {
603 my @types = qw(Cc AdminCc Requestor Owner);
605 foreach my $type (@types) {
606 my $type_obj = RT::Group->new($self->CurrentUser);
607 my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id,
609 Domain => 'RT::Queue-Role');
611 $RT::Logger->error("Couldn't create a Queue group of type '$type' for ticket ".
612 $self->Id.": ".$msg);
627 AddWatcher takes a parameter hash. The keys are as follows:
629 Type One of Requestor, Cc, AdminCc
631 PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
632 Email The email address of the new watcher. If a user with this
633 email address can't be found, a new nonprivileged user will be created.
635 If the watcher you\'re trying to set has an RT account, set the Owner paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
637 Returns a tuple of (status/id, message).
645 PrincipalId => undef,
651 #If the watcher we're trying to add is for the current user
652 if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
653 # If it's an AdminCc and they don't have
654 # 'WatchAsAdminCc' or 'ModifyTicket', bail
655 if ( $args{'Type'} eq 'AdminCc' ) {
656 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
657 or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
658 return ( 0, $self->loc('Permission Denied'))
662 # If it's a Requestor or Cc and they don't have
663 # 'Watch' or 'ModifyTicket', bail
664 elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
666 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
667 or $self->CurrentUserHasRight('Watch') ) {
668 return ( 0, $self->loc('Permission Denied'))
672 $RT::Logger->warning( "$self -> AddWatcher got passed a bogus type");
673 return ( 0, $self->loc('Error in parameters to Queue->AddWatcher') );
677 # If the watcher isn't the current user
678 # and the current user doesn't have 'ModifyQueueWatcher'
681 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
682 return ( 0, $self->loc("Permission Denied") );
688 return ( $self->_AddWatcher(%args) );
691 #This contains the meat of AddWatcher. but can be called from a routine like
692 # Create, which doesn't need the additional acl check
698 PrincipalId => undef,
704 my $principal = RT::Principal->new($self->CurrentUser);
705 if ($args{'PrincipalId'}) {
706 $principal->Load($args{'PrincipalId'});
708 elsif ($args{'Email'}) {
710 my $user = RT::User->new($self->CurrentUser);
711 $user->LoadByEmail($args{'Email'});
714 $user->Load($args{'Email'});
716 if ($user->Id) { # If the user exists
717 $principal->Load($user->PrincipalId);
720 # if the user doesn't exist, we need to create a new user
721 my $new_user = RT::User->new($RT::SystemUser);
723 my ( $Address, $Name ) =
724 RT::Interface::Email::ParseAddressFromHeader($args{'Email'});
726 my ( $Val, $Message ) = $new_user->Create(
728 EmailAddress => $Address,
731 Comments => 'Autocreated when added as a watcher');
733 $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message);
734 # Deal with the race condition of two account creations at once
735 $new_user->LoadByEmail($args{'Email'});
737 $principal->Load($new_user->PrincipalId);
740 # If we can't find this watcher, we need to bail.
741 unless ($principal->Id) {
742 return(0, $self->loc("Could not find or create that user"));
746 my $group = RT::Group->new($self->CurrentUser);
747 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
748 unless ($group->id) {
749 return(0,$self->loc("Group not found"));
752 if ( $group->HasMember( $principal)) {
754 return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
758 my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
760 $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
762 return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
764 return ( 1, $self->loc('Added principal as a [_1] for this queue', $args{'Type'}) );
769 # {{{ sub DeleteWatcher
771 =head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
774 Deletes a queue watcher. Takes two arguments:
776 Type (one of Requestor,Cc,AdminCc)
780 PrincipalId (an RT::Principal Id of the watcher you want to remove)
782 Email (the email address of an existing wathcer)
791 my %args = ( Type => undef,
792 PrincipalId => undef,
795 unless ($args{'PrincipalId'} ) {
796 return(0, $self->loc("No principal specified"));
798 my $principal = RT::Principal->new($self->CurrentUser);
799 $principal->Load($args{'PrincipalId'});
801 # If we can't find this watcher, we need to bail.
802 unless ($principal->Id) {
803 return(0, $self->loc("Could not find that principal"));
806 my $group = RT::Group->new($self->CurrentUser);
807 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
808 unless ($group->id) {
809 return(0,$self->loc("Group not found"));
813 #If the watcher we're trying to add is for the current user
814 if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
815 # If it's an AdminCc and they don't have
816 # 'WatchAsAdminCc' or 'ModifyQueue', bail
817 if ( $args{'Type'} eq 'AdminCc' ) {
818 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
819 or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
820 return ( 0, $self->loc('Permission Denied'))
824 # If it's a Requestor or Cc and they don't have
825 # 'Watch' or 'ModifyQueue', bail
826 elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
827 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
828 or $self->CurrentUserHasRight('Watch') ) {
829 return ( 0, $self->loc('Permission Denied'))
833 $RT::Logger->warning( "$self -> DeleteWatcher got passed a bogus type");
834 return ( 0, $self->loc('Error in parameters to Queue->DeleteWatcher') );
838 # If the watcher isn't the current user
839 # and the current user doesn't have 'ModifyQueueWathcers' bail
841 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
842 return ( 0, $self->loc("Permission Denied") );
849 # see if this user is already a watcher.
851 unless ( $group->HasMember($principal)) {
853 $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
856 my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
858 $RT::Logger->error("Failed to delete ".$principal->Id.
859 " as a member of group ".$group->Id."\n".$m_msg);
861 return ( 0, $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
864 return ( 1, $self->loc("[_1] is no longer a [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
869 # {{{ AdminCcAddresses
871 =head2 AdminCcAddresses
873 returns String: All queue AdminCc email addresses as a string
877 sub AdminCcAddresses {
880 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
884 return ( $self->AdminCc->MemberEmailAddressesAsString )
894 returns String: All queue Ccs as a string of email addresses
901 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
905 return ( $self->Cc->MemberEmailAddressesAsString);
916 Returns an RT::Group object which contains this Queue's Ccs.
917 If the user doesn't have "ShowQueue" permission, returns an empty group
924 my $group = RT::Group->new($self->CurrentUser);
925 if ( $self->CurrentUserHasRight('SeeQueue') ) {
926 $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
939 Returns an RT::Group object which contains this Queue's AdminCcs.
940 If the user doesn't have "ShowQueue" permission, returns an empty group
947 my $group = RT::Group->new($self->CurrentUser);
948 if ( $self->CurrentUserHasRight('SeeQueue') ) {
949 $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
957 # {{{ IsWatcher, IsCc, IsAdminCc
960 # a generic routine to be called by IsRequestor, IsCc and IsAdminCc
962 =head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
964 Takes a param hash with the attributes Type and PrincipalId
966 Type is one of Requestor, Cc, AdminCc and Owner
968 PrincipalId is an RT::Principal id
970 Returns true if that principal is a member of the group Type for this queue
978 my %args = ( Type => 'Cc',
979 PrincipalId => undef,
983 # Load the relevant group.
984 my $group = RT::Group->new($self->CurrentUser);
985 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->id);
986 # Ask if it has the member in question
988 my $principal = RT::Principal->new($self->CurrentUser);
989 $principal->Load($args{'PrincipalId'});
990 unless ($principal->Id) {
994 return ($group->HasMemberRecursively($principal));
1002 =head2 IsCc PRINCIPAL_ID
1004 Takes an RT::Principal id.
1005 Returns true if the principal is a requestor of the current queue.
1014 return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
1022 =head2 IsAdminCc PRINCIPAL_ID
1024 Takes an RT::Principal id.
1025 Returns true if the principal is a requestor of the current queue.
1033 return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
1048 # {{{ ACCESS CONTROL
1054 unless ( $self->CurrentUserHasRight('AdminQueue') ) {
1055 return ( 0, $self->loc('Permission Denied') );
1057 return ( $self->SUPER::_Set(@_) );
1067 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
1071 return ( $self->__Value(@_) );
1076 # {{{ sub CurrentUserHasRight
1078 =head2 CurrentUserHasRight
1080 Takes one argument. A textual string with the name of the right we want to check.
1081 Returns true if the current user has that right for this queue.
1082 Returns undef otherwise.
1086 sub CurrentUserHasRight {
1092 Principal => $self->CurrentUser,
1105 Takes a param hash with the fields 'Right' and 'Principal'.
1106 Principal defaults to the current user.
1107 Returns true if the principal has that right for this queue.
1108 Returns undef otherwise.
1112 # TAKES: Right and optional "Principal" which defaults to the current user
1117 Principal => $self->CurrentUser,
1120 unless ( defined $args{'Principal'} ) {
1121 $RT::Logger->debug("Principal undefined in Queue::HasRight");
1125 $args{'Principal'}->HasRight(
1126 Object => $self->Id ? $self : $RT::System,
1127 Right => $args{'Right'}