3 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
5 # (Except where explictly superceded by other copyright notices)
7 # This work is made available to you under the terms of Version 2 of
8 # the GNU General Public License. A copy of that license should have
9 # been provided with this software, but in any event can be snarfed
12 # This work is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
17 # Unless otherwise specified, all modifications, corrections or
18 # extensions to this work which alter its source code become the
19 # property of Best Practical Solutions, LLC when submitted for
20 # inclusion in the work.
26 RT::Queue - an RT Queue object
46 no warnings qw(redefine);
48 use vars qw(@STATUS @ACTIVE_STATUS @INACTIVE_STATUS $RIGHTS);
53 @ACTIVE_STATUS = qw(new open stalled);
54 @INACTIVE_STATUS = qw(resolved rejected deleted);
55 @STATUS = (@ACTIVE_STATUS, @INACTIVE_STATUS);
57 # $self->loc('new'); # For the string extractor to get a string to localize
58 # $self->loc('open'); # For the string extractor to get a string to localize
59 # $self->loc('stalled'); # For the string extractor to get a string to localize
60 # $self->loc('resolved'); # For the string extractor to get a string to localize
61 # $self->loc('rejected'); # For the string extractor to get a string to localize
62 # $self->loc('deleted'); # For the string extractor to get a string to localize
66 SeeQueue => 'Can this principal see this queue', # loc_pair
67 AdminQueue => 'Create, delete and modify queues', # loc_pair
68 ShowACL => 'Display Access Control List', # loc_pair
69 ModifyACL => 'Modify Access Control List', # loc_pair
70 ModifyQueueWatchers => 'Modify the queue watchers', # loc_pair
71 AdminCustomFields => 'Create, delete and modify custom fields', # loc_pair
72 ModifyTemplate => 'Modify Scrip templates for this queue', # loc_pair
73 ShowTemplate => 'Display Scrip templates for this queue', # loc_pair
75 ModifyScrips => 'Modify Scrips for this queue', # loc_pair
76 ShowScrips => 'Display Scrips for this queue', # loc_pair
78 ShowTicket => 'Show ticket summaries', # loc_pair
79 ShowTicketComments => 'Show ticket private commentary', # loc_pair
81 Watch => 'Sign up as a ticket Requestor or ticket or queue Cc', # loc_pair
82 WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc', # loc_pair
83 CreateTicket => 'Create tickets in this queue', # loc_pair
84 ReplyToTicket => 'Reply to tickets', # loc_pair
85 CommentOnTicket => 'Comment on tickets', # loc_pair
86 OwnTicket => 'Own tickets', # loc_pair
87 ModifyTicket => 'Modify tickets', # loc_pair
88 DeleteTicket => 'Delete tickets', # loc_pair
89 TakeTicket => 'Take tickets', # loc_pair
90 StealTicket => 'Steal tickets', # loc_pair
94 # Tell RT::ACE that this sort of object can get acls granted
95 $RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
97 # TODO: This should be refactored out into an RT::ACLedObject or something
98 # stuff the rights into a hash of rights that can exist.
100 foreach my $right ( keys %{$RIGHTS} ) {
101 $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
105 =head2 AvailableRights
107 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
111 sub AvailableRights {
116 # {{{ ActiveStatusArray
118 =head2 ActiveStatusArray
120 Returns an array of all ActiveStatuses for this queue
124 sub ActiveStatusArray {
126 return (@ACTIVE_STATUS);
131 # {{{ InactiveStatusArray
133 =head2 InactiveStatusArray
135 Returns an array of all InactiveStatuses for this queue
139 sub InactiveStatusArray {
141 return (@INACTIVE_STATUS);
150 Returns an array of all statuses for this queue
163 =head2 IsValidStatus VALUE
165 Returns true if VALUE is a valid status. Otherwise, returns 0
168 my $q = RT::Queue->new($RT::SystemUser);
169 ok($q->IsValidStatus('new')== 1, 'New is a valid status');
170 ok($q->IsValidStatus('f00')== 0, 'f00 is not a valid status');
178 my $retval = grep ( /^$value$/, $self->StatusArray );
187 =head2 IsActiveStatus VALUE
189 Returns true if VALUE is a Active status. Otherwise, returns 0
192 my $q = RT::Queue->new($RT::SystemUser);
193 ok($q->IsActiveStatus('new')== 1, 'New is a Active status');
194 ok($q->IsActiveStatus('rejected')== 0, 'Rejected is an inactive status');
195 ok($q->IsActiveStatus('f00')== 0, 'f00 is not a Active status');
203 my $retval = grep ( /^$value$/, $self->ActiveStatusArray );
210 # {{{ IsInactiveStatus
212 =head2 IsInactiveStatus VALUE
214 Returns true if VALUE is a Inactive status. Otherwise, returns 0
217 my $q = RT::Queue->new($RT::SystemUser);
218 ok($q->IsInactiveStatus('new')== 0, 'New is a Active status');
219 ok($q->IsInactiveStatus('rejected')== 1, 'rejeected is an Inactive status');
220 ok($q->IsInactiveStatus('f00')== 0, 'f00 is not a Active status');
224 sub IsInactiveStatus {
228 my $retval = grep ( /^$value$/, $self->InactiveStatusArray );
240 Create takes the name of the new queue
241 If you pass the ACL check, it creates the queue and returns its queue id.
249 CorrespondAddress => '',
251 CommentAddress => '',
252 InitialPriority => "0",
253 FinalPriority => "0",
258 unless ( $self->CurrentUser->HasRight(Right => 'AdminQueue', Object => $RT::System) )
260 return ( 0, $self->loc("No permission to create queues") );
263 unless ( $self->ValidateName( $args{'Name'} ) ) {
264 return ( 0, $self->loc('Queue already exists') );
267 #TODO better input validation
268 $RT::Handle->BeginTransaction();
270 my $id = $self->SUPER::Create(%args);
272 $RT::Handle->Rollback();
273 return ( 0, $self->loc('Queue could not be created') );
276 my $create_ret = $self->_CreateQueueGroups();
277 unless ($create_ret) {
278 $RT::Handle->Rollback();
279 return ( 0, $self->loc('Queue could not be created') );
282 $RT::Handle->Commit();
283 return ( $id, $self->loc("Queue created") );
293 $self->loc('Deleting this object would break referential integrity') );
298 # {{{ sub SetDisabled
303 1 will cause this queue to no longer be avaialble for tickets.
304 0 will re-enable this queue
314 Takes either a numerical id or a textual Name and loads the specified queue.
321 my $identifier = shift;
322 if ( !$identifier ) {
326 if ( $identifier =~ /^(\d+)$/ ) {
327 $self->SUPER::LoadById($identifier);
330 $self->LoadByCols( Name => $identifier );
333 return ( $self->Id );
339 # {{{ sub ValidateName
341 =head2 ValidateName NAME
343 Takes a queue name. Returns true if it's an ok name for
344 a new queue. Returns undef if there's already a queue by that name.
352 my $tempqueue = new RT::Queue($RT::SystemUser);
353 $tempqueue->Load($name);
355 #If we couldn't load it :)
356 unless ( $tempqueue->id() ) {
360 #If this queue exists, return undef
361 #Avoid the ACL check.
362 if ( $tempqueue->Name() ) {
366 #If the queue doesn't exist, return 1
379 Returns an RT::Templates object of all of this queue's templates.
386 my $templates = RT::Templates->new( $self->CurrentUser );
388 if ( $self->CurrentUserHasRight('ShowTemplate') ) {
389 $templates->LimitToQueue( $self->id );
397 # {{{ Dealing with custom fields
401 =item CustomField NAME
403 Load the queue-specific custom field named NAME
410 my $cf = RT::CustomField->new($self->CurrentUser);
411 $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id);
420 Returns an RT::CustomFields object containing all global custom fields, as well as those tied to this queue
427 my $cfs = RT::CustomFields->new( $self->CurrentUser );
428 if ( $self->CurrentUserHasRight('SeeQueue') ) {
429 $cfs->LimitToGlobalOrQueue( $self->Id );
439 # {{{ Routines dealing with watchers.
441 # {{{ _CreateQueueGroups
443 =head2 _CreateQueueGroups
445 Create the ticket groups and relationships for this ticket.
446 This routine expects to be called from Ticket->Create _inside of a transaction_
448 It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
450 It will return true on success and undef on failure.
454 my $Queue = RT::Queue->new($RT::SystemUser); my ($id, $msg) = $Queue->Create(Name => "Foo",
456 ok ($id, "Foo $id was created");
457 ok(my $group = RT::Group->new($RT::SystemUser));
458 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
459 ok ($group->Id, "Found the requestors object for this Queue");
462 ok ((my $add_id, $add_msg) = $Queue->AddWatcher(Type => 'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
463 ok ($add_id, "Add succeeded: ($add_msg)");
464 ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user");
465 $bob->LoadByEmail('bob@fsck.com');
466 ok($bob->Id, "Found the bob rt user");
467 ok ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $bob->PrincipalId), "The Queue actually has bob at fsck.com as a requestor");;
468 ok ((my $add_id, $add_msg) = $Queue->DeleteWatcher(Type =>'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
469 ok (!$Queue->IsWatcher(Type => 'Cc', Principal => $bob->PrincipalId), "The Queue no longer has bob at fsck.com as a requestor");;
472 $group = RT::Group->new($RT::SystemUser);
473 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
474 ok ($group->Id, "Found the cc object for this Queue");
475 $group = RT::Group->new($RT::SystemUser);
476 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'AdminCc'));
477 ok ($group->Id, "Found the AdminCc object for this Queue");
484 sub _CreateQueueGroups {
487 my @types = qw(Cc AdminCc Requestor Owner);
489 foreach my $type (@types) {
490 my $type_obj = RT::Group->new($self->CurrentUser);
491 my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id,
493 Domain => 'RT::Queue-Role');
495 $RT::Logger->error("Couldn't create a Queue group of type '$type' for ticket ".
496 $self->Id.": ".$msg);
511 AddWatcher takes a parameter hash. The keys are as follows:
513 Type One of Requestor, Cc, AdminCc
515 PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
516 Email The email address of the new watcher. If a user with this
517 email address can't be found, a new nonprivileged user will be created.
519 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.
527 PrincipalId => undef,
533 #If the watcher we're trying to add is for the current user
534 if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
535 # If it's an AdminCc and they don't have
536 # 'WatchAsAdminCc' or 'ModifyTicket', bail
537 if ( $args{'Type'} eq 'AdminCc' ) {
538 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
539 or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
540 return ( 0, $self->loc('Permission Denied'))
544 # If it's a Requestor or Cc and they don't have
545 # 'Watch' or 'ModifyTicket', bail
546 elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
548 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
549 or $self->CurrentUserHasRight('Watch') ) {
550 return ( 0, $self->loc('Permission Denied'))
554 $RT::Logger->warn( "$self -> AddWatcher got passed a bogus type");
555 return ( 0, $self->loc('Error in parameters to Queue->AddWatcher') );
559 # If the watcher isn't the current user
560 # and the current user doesn't have 'ModifyQueueWatcher'
563 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
564 return ( 0, $self->loc("Permission Denied") );
570 return ( $self->_AddWatcher(%args) );
573 #This contains the meat of AddWatcher. but can be called from a routine like
574 # Create, which doesn't need the additional acl check
580 PrincipalId => undef,
586 my $principal = RT::Principal->new($self->CurrentUser);
587 if ($args{'PrincipalId'}) {
588 $principal->Load($args{'PrincipalId'});
590 elsif ($args{'Email'}) {
592 my $user = RT::User->new($self->CurrentUser);
593 $user->LoadByEmail($args{'Email'});
596 $user->Load($args{'Email'});
598 if ($user->Id) { # If the user exists
599 $principal->Load($user->PrincipalId);
602 # if the user doesn't exist, we need to create a new user
603 my $new_user = RT::User->new($RT::SystemUser);
605 my ( $Val, $Message ) = $new_user->Create(
606 Name => $args{'Email'},
607 EmailAddress => $args{'Email'},
608 RealName => $args{'Email'},
610 Comments => 'Autocreated when added as a watcher');
612 $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message);
613 # Deal with the race condition of two account creations at once
614 $new_user->LoadByEmail($args{'Email'});
616 $principal->Load($new_user->PrincipalId);
619 # If we can't find this watcher, we need to bail.
620 unless ($principal->Id) {
621 return(0, $self->loc("Could not find or create that user"));
625 my $group = RT::Group->new($self->CurrentUser);
626 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
627 unless ($group->id) {
628 return(0,$self->loc("Group not found"));
631 if ( $group->HasMember( $principal)) {
633 return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
637 my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
639 $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
641 return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
643 return ( 1, $self->loc('Added principal as a [_1] for this queue', $args{'Type'}) );
648 # {{{ sub DeleteWatcher
650 =head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
653 Deletes a queue watcher. Takes two arguments:
655 Type (one of Requestor,Cc,AdminCc)
659 PrincipalId (an RT::Principal Id of the watcher you want to remove)
661 Email (the email address of an existing wathcer)
670 my %args = ( Type => undef,
671 PrincipalId => undef,
674 unless ($args{'PrincipalId'} ) {
675 return(0, $self->loc("No principal specified"));
677 my $principal = RT::Principal->new($self->CurrentUser);
678 $principal->Load($args{'PrincipalId'});
680 # If we can't find this watcher, we need to bail.
681 unless ($principal->Id) {
682 return(0, $self->loc("Could not find that principal"));
685 my $group = RT::Group->new($self->CurrentUser);
686 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
687 unless ($group->id) {
688 return(0,$self->loc("Group not found"));
692 #If the watcher we're trying to add is for the current user
693 if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
694 # If it's an AdminCc and they don't have
695 # 'WatchAsAdminCc' or 'ModifyQueue', bail
696 if ( $args{'Type'} eq 'AdminCc' ) {
697 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
698 or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
699 return ( 0, $self->loc('Permission Denied'))
703 # If it's a Requestor or Cc and they don't have
704 # 'Watch' or 'ModifyQueue', bail
705 elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
706 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
707 or $self->CurrentUserHasRight('Watch') ) {
708 return ( 0, $self->loc('Permission Denied'))
712 $RT::Logger->warn( "$self -> DelWatcher got passed a bogus type");
713 return ( 0, $self->loc('Error in parameters to Queue->DelWatcher') );
717 # If the watcher isn't the current user
718 # and the current user doesn't have 'ModifyQueueWathcers' bail
720 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
721 return ( 0, $self->loc("Permission Denied") );
728 # see if this user is already a watcher.
730 unless ( $group->HasMember($principal)) {
732 $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
735 my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
737 $RT::Logger->error("Failed to delete ".$principal->Id.
738 " as a member of group ".$group->Id."\n".$m_msg);
740 return ( 0, $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
743 return ( 1, $self->loc("[_1] is no longer a [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
748 # {{{ AdminCcAddresses
750 =head2 AdminCcAddresses
752 returns String: All queue AdminCc email addresses as a string
756 sub AdminCcAddresses {
759 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
763 return ( $self->AdminCc->MemberEmailAddressesAsString )
773 returns String: All queue Ccs as a string of email addresses
780 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
784 return ( $self->Cc->MemberEmailAddressesAsString);
795 Returns an RT::Group object which contains this Queue's Ccs.
796 If the user doesn't have "ShowQueue" permission, returns an empty group
803 my $group = RT::Group->new($self->CurrentUser);
804 if ( $self->CurrentUserHasRight('SeeQueue') ) {
805 $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
818 Returns an RT::Group object which contains this Queue's AdminCcs.
819 If the user doesn't have "ShowQueue" permission, returns an empty group
826 my $group = RT::Group->new($self->CurrentUser);
827 if ( $self->CurrentUserHasRight('SeeQueue') ) {
828 $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
836 # {{{ IsWatcher, IsCc, IsAdminCc
839 # a generic routine to be called by IsRequestor, IsCc and IsAdminCc
841 =head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
843 Takes a param hash with the attributes Type and PrincipalId
845 Type is one of Requestor, Cc, AdminCc and Owner
847 PrincipalId is an RT::Principal id
849 Returns true if that principal is a member of the group Type for this queue
857 my %args = ( Type => 'Cc',
858 PrincipalId => undef,
862 # Load the relevant group.
863 my $group = RT::Group->new($self->CurrentUser);
864 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->id);
865 # Ask if it has the member in question
867 my $principal = RT::Principal->new($self->CurrentUser);
868 $principal->Load($args{'PrincipalId'});
869 unless ($principal->Id) {
873 return ($group->HasMemberRecursively($principal));
881 =head2 IsCc PRINCIPAL_ID
883 Takes an RT::Principal id.
884 Returns true if the principal is a requestor of the current queue.
893 return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
901 =head2 IsAdminCc PRINCIPAL_ID
903 Takes an RT::Principal id.
904 Returns true if the principal is a requestor of the current queue.
912 return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
933 unless ( $self->CurrentUserHasRight('AdminQueue') ) {
934 return ( 0, $self->loc('Permission Denied') );
936 return ( $self->SUPER::_Set(@_) );
946 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
950 return ( $self->__Value(@_) );
955 # {{{ sub CurrentUserHasRight
957 =head2 CurrentUserHasRight
959 Takes one argument. A textual string with the name of the right we want to check.
960 Returns true if the current user has that right for this queue.
961 Returns undef otherwise.
965 sub CurrentUserHasRight {
971 Principal => $self->CurrentUser,
984 Takes a param hash with the fields 'Right' and 'Principal'.
985 Principal defaults to the current user.
986 Returns true if the principal has that right for this queue.
987 Returns undef otherwise.
991 # TAKES: Right and optional "Principal" which defaults to the current user
996 Principal => $self->CurrentUser,
999 unless ( defined $args{'Principal'} ) {
1000 $RT::Logger->debug("Principal undefined in Queue::HasRight");
1004 $args{'Principal'}->HasRight(
1006 Right => $args{'Right'}