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);
54 @ACTIVE_STATUS = qw(new open stalled);
55 @INACTIVE_STATUS = qw(resolved rejected deleted);
56 @STATUS = (@ACTIVE_STATUS, @INACTIVE_STATUS);
58 # $self->loc('new'); # For the string extractor to get a string to localize
59 # $self->loc('open'); # For the string extractor to get a string to localize
60 # $self->loc('stalled'); # For the string extractor to get a string to localize
61 # $self->loc('resolved'); # For the string extractor to get a string to localize
62 # $self->loc('rejected'); # For the string extractor to get a string to localize
63 # $self->loc('deleted'); # For the string extractor to get a string to localize
67 SeeQueue => 'Can this principal see this queue', # loc_pair
68 AdminQueue => 'Create, delete and modify queues', # loc_pair
69 ShowACL => 'Display Access Control List', # loc_pair
70 ModifyACL => 'Modify Access Control List', # loc_pair
71 ModifyQueueWatchers => 'Modify the queue watchers', # loc_pair
72 AdminCustomFields => 'Create, delete and modify custom fields', # loc_pair
73 ModifyTemplate => 'Modify Scrip templates for this queue', # loc_pair
74 ShowTemplate => 'Display Scrip templates for this queue', # loc_pair
76 ModifyScrips => 'Modify Scrips for this queue', # loc_pair
77 ShowScrips => 'Display Scrips for this queue', # loc_pair
79 ShowTicket => 'Show ticket summaries', # loc_pair
80 ShowTicketComments => 'Show ticket private commentary', # loc_pair
82 Watch => 'Sign up as a ticket Requestor or ticket or queue Cc', # loc_pair
83 WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc', # loc_pair
84 CreateTicket => 'Create tickets in this queue', # loc_pair
85 ReplyToTicket => 'Reply to tickets', # loc_pair
86 CommentOnTicket => 'Comment on tickets', # loc_pair
87 OwnTicket => 'Own tickets', # loc_pair
88 ModifyTicket => 'Modify tickets', # loc_pair
89 DeleteTicket => 'Delete tickets', # loc_pair
90 TakeTicket => 'Take tickets', # loc_pair
91 StealTicket => 'Steal tickets', # loc_pair
95 # Tell RT::ACE that this sort of object can get acls granted
96 $RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
98 # TODO: This should be refactored out into an RT::ACLedObject or something
99 # stuff the rights into a hash of rights that can exist.
101 foreach my $right ( keys %{$RIGHTS} ) {
102 $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
106 =head2 AvailableRights
108 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
112 sub AvailableRights {
117 # {{{ ActiveStatusArray
119 =head2 ActiveStatusArray
121 Returns an array of all ActiveStatuses for this queue
125 sub ActiveStatusArray {
127 return (@ACTIVE_STATUS);
132 # {{{ InactiveStatusArray
134 =head2 InactiveStatusArray
136 Returns an array of all InactiveStatuses for this queue
140 sub InactiveStatusArray {
142 return (@INACTIVE_STATUS);
151 Returns an array of all statuses for this queue
164 =head2 IsValidStatus VALUE
166 Returns true if VALUE is a valid status. Otherwise, returns 0
169 my $q = RT::Queue->new($RT::SystemUser);
170 ok($q->IsValidStatus('new')== 1, 'New is a valid status');
171 ok($q->IsValidStatus('f00')== 0, 'f00 is not a valid status');
179 my $retval = grep ( /^$value$/, $self->StatusArray );
188 =head2 IsActiveStatus VALUE
190 Returns true if VALUE is a Active status. Otherwise, returns 0
193 my $q = RT::Queue->new($RT::SystemUser);
194 ok($q->IsActiveStatus('new')== 1, 'New is a Active status');
195 ok($q->IsActiveStatus('rejected')== 0, 'Rejected is an inactive status');
196 ok($q->IsActiveStatus('f00')== 0, 'f00 is not a Active status');
204 my $retval = grep ( /^$value$/, $self->ActiveStatusArray );
211 # {{{ IsInactiveStatus
213 =head2 IsInactiveStatus VALUE
215 Returns true if VALUE is a Inactive status. Otherwise, returns 0
218 my $q = RT::Queue->new($RT::SystemUser);
219 ok($q->IsInactiveStatus('new')== 0, 'New is a Active status');
220 ok($q->IsInactiveStatus('rejected')== 1, 'rejeected is an Inactive status');
221 ok($q->IsInactiveStatus('f00')== 0, 'f00 is not a Active status');
225 sub IsInactiveStatus {
229 my $retval = grep ( /^$value$/, $self->InactiveStatusArray );
241 Create takes the name of the new queue
242 If you pass the ACL check, it creates the queue and returns its queue id.
250 CorrespondAddress => '',
252 CommentAddress => '',
253 InitialPriority => "0",
254 FinalPriority => "0",
259 unless ( $self->CurrentUser->HasRight(Right => 'AdminQueue', Object => $RT::System) )
261 return ( 0, $self->loc("No permission to create queues") );
264 unless ( $self->ValidateName( $args{'Name'} ) ) {
265 return ( 0, $self->loc('Queue already exists') );
268 #TODO better input validation
269 $RT::Handle->BeginTransaction();
271 my $id = $self->SUPER::Create(%args);
273 $RT::Handle->Rollback();
274 return ( 0, $self->loc('Queue could not be created') );
277 my $create_ret = $self->_CreateQueueGroups();
278 unless ($create_ret) {
279 $RT::Handle->Rollback();
280 return ( 0, $self->loc('Queue could not be created') );
283 $RT::Handle->Commit();
284 return ( $id, $self->loc("Queue created") );
294 $self->loc('Deleting this object would break referential integrity') );
299 # {{{ sub SetDisabled
304 1 will cause this queue to no longer be avaialble for tickets.
305 0 will re-enable this queue
315 Takes either a numerical id or a textual Name and loads the specified queue.
322 my $identifier = shift;
323 if ( !$identifier ) {
327 if ( $identifier =~ /^(\d+)$/ ) {
328 $self->SUPER::LoadById($identifier);
331 $self->LoadByCols( Name => $identifier );
334 return ( $self->Id );
340 # {{{ sub ValidateName
342 =head2 ValidateName NAME
344 Takes a queue name. Returns true if it's an ok name for
345 a new queue. Returns undef if there's already a queue by that name.
353 my $tempqueue = new RT::Queue($RT::SystemUser);
354 $tempqueue->Load($name);
356 #If we couldn't load it :)
357 unless ( $tempqueue->id() ) {
361 #If this queue exists, return undef
362 #Avoid the ACL check.
363 if ( $tempqueue->Name() ) {
367 #If the queue doesn't exist, return 1
380 Returns an RT::Templates object of all of this queue's templates.
387 my $templates = RT::Templates->new( $self->CurrentUser );
389 if ( $self->CurrentUserHasRight('ShowTemplate') ) {
390 $templates->LimitToQueue( $self->id );
398 # {{{ Dealing with custom fields
402 =item CustomField NAME
404 Load the queue-specific custom field named NAME
411 my $cf = RT::CustomField->new($self->CurrentUser);
412 $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id);
421 Returns an RT::CustomFields object containing all global custom fields, as well as those tied to this queue
428 my $cfs = RT::CustomFields->new( $self->CurrentUser );
429 if ( $self->CurrentUserHasRight('SeeQueue') ) {
430 $cfs->LimitToGlobalOrQueue( $self->Id );
440 # {{{ Routines dealing with watchers.
442 # {{{ _CreateQueueGroups
444 =head2 _CreateQueueGroups
446 Create the ticket groups and relationships for this ticket.
447 This routine expects to be called from Ticket->Create _inside of a transaction_
449 It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
451 It will return true on success and undef on failure.
455 my $Queue = RT::Queue->new($RT::SystemUser); my ($id, $msg) = $Queue->Create(Name => "Foo",
457 ok ($id, "Foo $id was created");
458 ok(my $group = RT::Group->new($RT::SystemUser));
459 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
460 ok ($group->Id, "Found the requestors object for this Queue");
463 ok ((my $add_id, $add_msg) = $Queue->AddWatcher(Type => 'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
464 ok ($add_id, "Add succeeded: ($add_msg)");
465 ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user");
466 $bob->LoadByEmail('bob@fsck.com');
467 ok($bob->Id, "Found the bob rt user");
468 ok ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $bob->PrincipalId), "The Queue actually has bob at fsck.com as a requestor");;
469 ok ((my $add_id, $add_msg) = $Queue->DeleteWatcher(Type =>'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
470 ok (!$Queue->IsWatcher(Type => 'Cc', Principal => $bob->PrincipalId), "The Queue no longer has bob at fsck.com as a requestor");;
473 $group = RT::Group->new($RT::SystemUser);
474 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
475 ok ($group->Id, "Found the cc object for this Queue");
476 $group = RT::Group->new($RT::SystemUser);
477 ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'AdminCc'));
478 ok ($group->Id, "Found the AdminCc object for this Queue");
485 sub _CreateQueueGroups {
488 my @types = qw(Cc AdminCc Requestor Owner);
490 foreach my $type (@types) {
491 my $type_obj = RT::Group->new($self->CurrentUser);
492 my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id,
494 Domain => 'RT::Queue-Role');
496 $RT::Logger->error("Couldn't create a Queue group of type '$type' for ticket ".
497 $self->Id.": ".$msg);
512 AddWatcher takes a parameter hash. The keys are as follows:
514 Type One of Requestor, Cc, AdminCc
516 PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
517 Email The email address of the new watcher. If a user with this
518 email address can't be found, a new nonprivileged user will be created.
520 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.
522 Returns a tuple of (status/id, message).
530 PrincipalId => undef,
536 #If the watcher we're trying to add is for the current user
537 if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
538 # If it's an AdminCc and they don't have
539 # 'WatchAsAdminCc' or 'ModifyTicket', bail
540 if ( $args{'Type'} eq 'AdminCc' ) {
541 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
542 or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
543 return ( 0, $self->loc('Permission Denied'))
547 # If it's a Requestor or Cc and they don't have
548 # 'Watch' or 'ModifyTicket', bail
549 elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
551 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
552 or $self->CurrentUserHasRight('Watch') ) {
553 return ( 0, $self->loc('Permission Denied'))
557 $RT::Logger->warn( "$self -> AddWatcher got passed a bogus type");
558 return ( 0, $self->loc('Error in parameters to Queue->AddWatcher') );
562 # If the watcher isn't the current user
563 # and the current user doesn't have 'ModifyQueueWatcher'
566 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
567 return ( 0, $self->loc("Permission Denied") );
573 return ( $self->_AddWatcher(%args) );
576 #This contains the meat of AddWatcher. but can be called from a routine like
577 # Create, which doesn't need the additional acl check
583 PrincipalId => undef,
589 my $principal = RT::Principal->new($self->CurrentUser);
590 if ($args{'PrincipalId'}) {
591 $principal->Load($args{'PrincipalId'});
593 elsif ($args{'Email'}) {
595 my $user = RT::User->new($self->CurrentUser);
596 $user->LoadByEmail($args{'Email'});
599 $user->Load($args{'Email'});
601 if ($user->Id) { # If the user exists
602 $principal->Load($user->PrincipalId);
605 # if the user doesn't exist, we need to create a new user
606 my $new_user = RT::User->new($RT::SystemUser);
608 my ( $Address, $Name ) =
609 RT::EmailParser::ParseAddressFromHeader('', $args{'Email'});
611 my ( $Val, $Message ) = $new_user->Create(
613 EmailAddress => $Address,
616 Comments => 'Autocreated when added as a watcher');
618 $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message);
619 # Deal with the race condition of two account creations at once
620 $new_user->LoadByEmail($args{'Email'});
622 $principal->Load($new_user->PrincipalId);
625 # If we can't find this watcher, we need to bail.
626 unless ($principal->Id) {
627 return(0, $self->loc("Could not find or create that user"));
631 my $group = RT::Group->new($self->CurrentUser);
632 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
633 unless ($group->id) {
634 return(0,$self->loc("Group not found"));
637 if ( $group->HasMember( $principal)) {
639 return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
643 my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
645 $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
647 return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
649 return ( 1, $self->loc('Added principal as a [_1] for this queue', $args{'Type'}) );
654 # {{{ sub DeleteWatcher
656 =head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
659 Deletes a queue watcher. Takes two arguments:
661 Type (one of Requestor,Cc,AdminCc)
665 PrincipalId (an RT::Principal Id of the watcher you want to remove)
667 Email (the email address of an existing wathcer)
676 my %args = ( Type => undef,
677 PrincipalId => undef,
680 unless ($args{'PrincipalId'} ) {
681 return(0, $self->loc("No principal specified"));
683 my $principal = RT::Principal->new($self->CurrentUser);
684 $principal->Load($args{'PrincipalId'});
686 # If we can't find this watcher, we need to bail.
687 unless ($principal->Id) {
688 return(0, $self->loc("Could not find that principal"));
691 my $group = RT::Group->new($self->CurrentUser);
692 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
693 unless ($group->id) {
694 return(0,$self->loc("Group not found"));
698 #If the watcher we're trying to add is for the current user
699 if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
700 # If it's an AdminCc and they don't have
701 # 'WatchAsAdminCc' or 'ModifyQueue', bail
702 if ( $args{'Type'} eq 'AdminCc' ) {
703 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
704 or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
705 return ( 0, $self->loc('Permission Denied'))
709 # If it's a Requestor or Cc and they don't have
710 # 'Watch' or 'ModifyQueue', bail
711 elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
712 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
713 or $self->CurrentUserHasRight('Watch') ) {
714 return ( 0, $self->loc('Permission Denied'))
718 $RT::Logger->warn( "$self -> DelWatcher got passed a bogus type");
719 return ( 0, $self->loc('Error in parameters to Queue->DelWatcher') );
723 # If the watcher isn't the current user
724 # and the current user doesn't have 'ModifyQueueWathcers' bail
726 unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
727 return ( 0, $self->loc("Permission Denied") );
734 # see if this user is already a watcher.
736 unless ( $group->HasMember($principal)) {
738 $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
741 my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
743 $RT::Logger->error("Failed to delete ".$principal->Id.
744 " as a member of group ".$group->Id."\n".$m_msg);
746 return ( 0, $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
749 return ( 1, $self->loc("[_1] is no longer a [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
754 # {{{ AdminCcAddresses
756 =head2 AdminCcAddresses
758 returns String: All queue AdminCc email addresses as a string
762 sub AdminCcAddresses {
765 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
769 return ( $self->AdminCc->MemberEmailAddressesAsString )
779 returns String: All queue Ccs as a string of email addresses
786 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
790 return ( $self->Cc->MemberEmailAddressesAsString);
801 Returns an RT::Group object which contains this Queue's Ccs.
802 If the user doesn't have "ShowQueue" permission, returns an empty group
809 my $group = RT::Group->new($self->CurrentUser);
810 if ( $self->CurrentUserHasRight('SeeQueue') ) {
811 $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
824 Returns an RT::Group object which contains this Queue's AdminCcs.
825 If the user doesn't have "ShowQueue" permission, returns an empty group
832 my $group = RT::Group->new($self->CurrentUser);
833 if ( $self->CurrentUserHasRight('SeeQueue') ) {
834 $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
842 # {{{ IsWatcher, IsCc, IsAdminCc
845 # a generic routine to be called by IsRequestor, IsCc and IsAdminCc
847 =head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
849 Takes a param hash with the attributes Type and PrincipalId
851 Type is one of Requestor, Cc, AdminCc and Owner
853 PrincipalId is an RT::Principal id
855 Returns true if that principal is a member of the group Type for this queue
863 my %args = ( Type => 'Cc',
864 PrincipalId => undef,
868 # Load the relevant group.
869 my $group = RT::Group->new($self->CurrentUser);
870 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->id);
871 # Ask if it has the member in question
873 my $principal = RT::Principal->new($self->CurrentUser);
874 $principal->Load($args{'PrincipalId'});
875 unless ($principal->Id) {
879 return ($group->HasMemberRecursively($principal));
887 =head2 IsCc PRINCIPAL_ID
889 Takes an RT::Principal id.
890 Returns true if the principal is a requestor of the current queue.
899 return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
907 =head2 IsAdminCc PRINCIPAL_ID
909 Takes an RT::Principal id.
910 Returns true if the principal is a requestor of the current queue.
918 return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
939 unless ( $self->CurrentUserHasRight('AdminQueue') ) {
940 return ( 0, $self->loc('Permission Denied') );
942 return ( $self->SUPER::_Set(@_) );
952 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
956 return ( $self->__Value(@_) );
961 # {{{ sub CurrentUserHasRight
963 =head2 CurrentUserHasRight
965 Takes one argument. A textual string with the name of the right we want to check.
966 Returns true if the current user has that right for this queue.
967 Returns undef otherwise.
971 sub CurrentUserHasRight {
977 Principal => $self->CurrentUser,
990 Takes a param hash with the fields 'Right' and 'Principal'.
991 Principal defaults to the current user.
992 Returns true if the principal has that right for this queue.
993 Returns undef otherwise.
997 # TAKES: Right and optional "Principal" which defaults to the current user
1002 Principal => $self->CurrentUser,
1005 unless ( defined $args{'Principal'} ) {
1006 $RT::Logger->debug("Principal undefined in Queue::HasRight");
1010 $args{'Principal'}->HasRight(
1012 Right => $args{'Right'}