import rt 3.4.6
[freeside.git] / rt / lib / RT / Queue_Overlay.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2
3 # COPYRIGHT:
4 #  
5 # This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
6 #                                          <jesse@bestpractical.com>
7
8 # (Except where explicitly superseded by other copyright notices)
9
10
11 # LICENSE:
12
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
16 # from www.gnu.org.
17
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.
22
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.
26
27
28 # CONTRIBUTION SUBMISSION POLICY:
29
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.)
35
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.
44
45 # END BPS TAGGED BLOCK }}}
46
47 =head1 NAME
48
49   RT::Queue - an RT Queue object
50
51 =head1 SYNOPSIS
52
53   use RT::Queue;
54
55 =head1 DESCRIPTION
56
57
58 =head1 METHODS
59
60 =begin testing 
61
62 use RT::Queue;
63
64 =end testing
65
66 =cut
67
68
69 package RT::Queue;
70
71 use strict;
72 no warnings qw(redefine);
73
74 use vars qw(@DEFAULT_ACTIVE_STATUS @DEFAULT_INACTIVE_STATUS $RIGHTS);
75
76 use RT::Groups;
77 use RT::ACL;
78 use RT::Interface::Email;
79
80 @DEFAULT_ACTIVE_STATUS = qw(new open stalled);
81 @DEFAULT_INACTIVE_STATUS = qw(resolved rejected deleted);  
82
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
89
90
91 $RIGHTS = {
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
100
101     ModifyScrips => 'Modify Scrips for this queue',                   # loc_pair
102     ShowScrips   => 'Display Scrips for this queue',                  # loc_pair
103
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
107
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
118
119 };
120
121 # Tell RT::ACE that this sort of object can get acls granted
122 $RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
123
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.
126
127 foreach my $right ( keys %{$RIGHTS} ) {
128     $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
129 }
130     
131
132 sub AddLink {
133     my $self = shift;
134     my %args = ( Target => '',
135                  Base   => '',
136                  Type   => '',
137                  Silent => undef,
138                  @_ );
139
140     unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
141         return ( 0, $self->loc("Permission Denied") );
142     }
143
144     return $self->SUPER::_AddLink(%args);
145 }
146
147 sub DeleteLink {
148     my $self = shift;
149     my %args = (
150         Base   => undef,
151         Target => undef,
152         Type   => undef,
153         @_
154     );
155
156     #check acls
157     unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
158         $RT::Logger->debug("No permission to delete links\n");
159         return ( 0, $self->loc('Permission Denied'))
160     }
161
162     return $self->SUPER::_DeleteLink(%args);
163 }
164
165 =head2 AvailableRights
166
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
168
169 =cut
170
171 sub AvailableRights {
172     my $self = shift;
173     return($RIGHTS);
174 }
175
176 # {{{ ActiveStatusArray
177
178 =head2 ActiveStatusArray
179
180 Returns an array of all ActiveStatuses for this queue
181
182 =cut
183
184 sub ActiveStatusArray {
185     my $self = shift;
186     if (@RT::ActiveStatus) {
187         return (@RT::ActiveStatus)
188     } else {
189         $RT::Logger->warning("RT::ActiveStatus undefined, falling back to deprecated defaults");
190         return (@DEFAULT_ACTIVE_STATUS);
191     }
192 }
193
194 # }}}
195
196 # {{{ InactiveStatusArray
197
198 =head2 InactiveStatusArray
199
200 Returns an array of all InactiveStatuses for this queue
201
202 =cut
203
204 sub InactiveStatusArray {
205     my $self = shift;
206     if (@RT::InactiveStatus) {
207         return (@RT::InactiveStatus)
208     } else {
209         $RT::Logger->warning("RT::InactiveStatus undefined, falling back to deprecated defaults");
210         return (@DEFAULT_INACTIVE_STATUS);
211     }
212 }
213
214 # }}}
215
216 # {{{ StatusArray
217
218 =head2 StatusArray
219
220 Returns an array of all statuses for this queue
221
222 =cut
223
224 sub StatusArray {
225     my $self = shift;
226     return ($self->ActiveStatusArray(), $self->InactiveStatusArray());
227 }
228
229 # }}}
230
231 # {{{ IsValidStatus
232
233 =head2 IsValidStatus VALUE
234
235 Returns true if VALUE is a valid status.  Otherwise, returns 0.
236
237 =begin testing
238
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');
242
243 =end testing
244
245 =cut
246
247 sub IsValidStatus {
248     my $self  = shift;
249     my $value = shift;
250
251     my $retval = grep ( $_ eq $value, $self->StatusArray );
252     return ($retval);
253
254 }
255
256 # }}}
257
258 # {{{ IsActiveStatus
259
260 =head2 IsActiveStatus VALUE
261
262 Returns true if VALUE is a Active status.  Otherwise, returns 0
263
264 =begin testing
265
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');
270
271 =end testing
272
273 =cut
274
275 sub IsActiveStatus {
276     my $self  = shift;
277     my $value = shift;
278
279     my $retval = grep ( $_ eq $value, $self->ActiveStatusArray );
280     return ($retval);
281
282 }
283
284 # }}}
285
286 # {{{ IsInactiveStatus
287
288 =head2 IsInactiveStatus VALUE
289
290 Returns true if VALUE is a Inactive status.  Otherwise, returns 0
291
292 =begin testing
293
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');
298
299 =end testing
300
301 =cut
302
303 sub IsInactiveStatus {
304     my $self  = shift;
305     my $value = shift;
306
307     my $retval = grep ( $_ eq $value, $self->InactiveStatusArray );
308     return ($retval);
309
310 }
311
312 # }}}
313
314
315 # {{{ sub Create
316
317
318
319
320 =head2 Create(ARGS)
321
322 Arguments: ARGS is a hash of named parameters.  Valid parameters are:
323
324   Name (required)
325   Description
326   CorrespondAddress
327   CommentAddress
328   InitialPriority
329   FinalPriority
330   DefaultDueIn
331  
332 If you pass the ACL check, it creates the queue and returns its queue id.
333
334 =begin testing
335
336 my $queue = RT::Queue->new($RT::SystemUser);
337 my ($id, $val) = $queue->Create( Name => 'Test1');
338 ok($id, $val);
339
340 ($id, $val) = $queue->Create( Name => '66');
341 ok(!$id, $val);
342
343 =end testing
344
345 =cut
346
347 sub Create {
348     my $self = shift;
349     my %args = (
350         Name              => undef,
351         CorrespondAddress => '',
352         Description       => '',
353         CommentAddress    => '',
354         InitialPriority   => "0",
355         FinalPriority     => "0",
356         DefaultDueIn      => "0",
357         @_
358     );
359
360     unless ( $self->CurrentUser->HasRight(Right => 'AdminQueue', Object => $RT::System) )
361     {    #Check them ACLs
362         return ( 0, $self->loc("No permission to create queues") );
363     }
364
365     unless ( $self->ValidateName( $args{'Name'} ) ) {
366         return ( 0, $self->loc('Queue already exists') );
367     }
368
369     #TODO better input validation
370     $RT::Handle->BeginTransaction();
371
372     my $id = $self->SUPER::Create(%args);
373     unless ($id) {
374         $RT::Handle->Rollback();
375         return ( 0, $self->loc('Queue could not be created') );
376     }
377
378     my $create_ret = $self->_CreateQueueGroups();
379     unless ($create_ret) {
380         $RT::Handle->Rollback();
381         return ( 0, $self->loc('Queue could not be created') );
382     }
383
384     $RT::Handle->Commit();
385     return ( $id, $self->loc("Queue created") );
386 }
387
388 # }}}
389
390 # {{{ sub Delete 
391
392 sub Delete {
393     my $self = shift;
394     return ( 0,
395         $self->loc('Deleting this object would break referential integrity') );
396 }
397
398 # }}}
399
400 # {{{ sub SetDisabled
401
402 =head2 SetDisabled
403
404 Takes a boolean.
405 1 will cause this queue to no longer be available for tickets.
406 0 will re-enable this queue.
407
408 =cut
409
410 # }}}
411
412 # {{{ sub Load 
413
414 =head2 Load
415
416 Takes either a numerical id or a textual Name and loads the specified queue.
417
418 =cut
419
420 sub Load {
421     my $self = shift;
422
423     my $identifier = shift;
424     if ( !$identifier ) {
425         return (undef);
426     }
427
428     if ( $identifier =~ /^(\d+)$/ ) {
429         $self->SUPER::LoadById($identifier);
430     }
431     else {
432         $self->LoadByCols( Name => $identifier );
433     }
434
435     return ( $self->Id );
436
437 }
438
439 # }}}
440
441 # {{{ sub ValidateName
442
443 =head2 ValidateName NAME
444
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.
447
448 =cut
449
450 sub ValidateName {
451     my $self = shift;
452     my $name = shift;
453
454     my $tempqueue = new RT::Queue($RT::SystemUser);
455     $tempqueue->Load($name);
456
457     #If this queue exists, return undef
458     if ( $tempqueue->Name() && $tempqueue->id != $self->id)  {
459         return (undef);
460     }
461
462     #If the queue doesn't exist, return 1
463     else {
464         return ($self->SUPER::ValidateName($name));
465     }
466
467 }
468
469 # }}}
470
471 # {{{ sub Templates
472
473 =head2 Templates
474
475 Returns an RT::Templates object of all of this queue's templates.
476
477 =cut
478
479 sub Templates {
480     my $self = shift;
481
482     my $templates = RT::Templates->new( $self->CurrentUser );
483
484     if ( $self->CurrentUserHasRight('ShowTemplate') ) {
485         $templates->LimitToQueue( $self->id );
486     }
487
488     return ($templates);
489 }
490
491 # }}}
492
493 # {{{ Dealing with custom fields
494
495 # {{{  CustomField
496
497 =head2 CustomField NAME
498
499 Load the queue-specific custom field named NAME
500
501 =cut
502
503 sub CustomField {
504     my $self = shift;
505     my $name = shift;
506     my $cf = RT::CustomField->new($self->CurrentUser);
507     $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id); 
508     return ($cf);
509 }
510
511
512 # {{{ CustomFields
513
514 =head2 CustomFields
515
516 Returns an RT::CustomFields object containing all global custom fields, as well as those tied to this queue
517
518 =cut
519
520 # XXX TODO - this should become TicketCustomFields
521
522 sub CustomFields {
523     my $self = shift;
524     warn "Queue->CustomFields is deprecated, use Queue->TicketCustomFields instead at (". join(":",caller).")";
525     return $self->TicketCustomFields(@_);
526 }
527
528 sub TicketCustomFields {
529     my $self = shift;
530
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' );
535     }
536     return ($cfs);
537 }
538
539 sub TicketTransactionCustomFields {
540     my $self = shift;
541
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' );
546     }
547     return ($cfs);
548 }
549
550 # }}}
551
552 # }}}
553
554
555 # {{{ Routines dealing with watchers.
556
557 # {{{ _CreateQueueGroups 
558
559 =head2 _CreateQueueGroups
560
561 Create the ticket groups and links for this ticket. 
562 This routine expects to be called from Ticket->Create _inside of a transaction_
563
564 It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
565
566 It will return true on success and undef on failure.
567
568 =begin testing
569
570 my $Queue = RT::Queue->new($RT::SystemUser); my ($id, $msg) = $Queue->Create(Name => "Foo",
571                 );
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");
576
577
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");;
586
587
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");
594
595 =end testing
596
597 =cut
598
599
600 sub _CreateQueueGroups {
601     my $self = shift;
602
603     my @types = qw(Cc AdminCc Requestor Owner);
604
605     foreach my $type (@types) {
606         my $type_obj = RT::Group->new($self->CurrentUser);
607         my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id, 
608                                                      Type => $type,
609                                                      Domain => 'RT::Queue-Role');
610         unless ($id) {
611             $RT::Logger->error("Couldn't create a Queue group of type '$type' for ticket ".
612                                $self->Id.": ".$msg);
613             return(undef);
614         }
615      }
616     return(1);
617    
618 }
619
620
621 # }}}
622
623 # {{{ sub AddWatcher
624
625 =head2 AddWatcher
626
627 AddWatcher takes a parameter hash. The keys are as follows:
628
629 Type        One of Requestor, Cc, AdminCc
630
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.
634
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.
636
637 Returns a tuple of (status/id, message).
638
639 =cut
640
641 sub AddWatcher {
642     my $self = shift;
643     my %args = (
644         Type  => undef,
645         PrincipalId => undef,
646         Email => undef,
647         @_
648     );
649
650     # {{{ Check ACLS
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'))
659             }
660         }
661
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' ) ) {
665
666             unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
667                 or $self->CurrentUserHasRight('Watch') ) {
668                 return ( 0, $self->loc('Permission Denied'))
669             }
670         }
671      else {
672             $RT::Logger->warning( "$self -> AddWatcher got passed a bogus type");
673             return ( 0, $self->loc('Error in parameters to Queue->AddWatcher') );
674         }
675     }
676
677     # If the watcher isn't the current user 
678     # and the current user  doesn't have 'ModifyQueueWatcher'
679     # bail
680     else {
681         unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
682             return ( 0, $self->loc("Permission Denied") );
683         }
684     }
685
686     # }}}
687
688     return ( $self->_AddWatcher(%args) );
689 }
690
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
693 sub _AddWatcher {
694     my $self = shift;
695     my %args = (
696         Type   => undef,
697         Silent => undef,
698         PrincipalId => undef,
699         Email => undef,
700         @_
701     );
702
703
704     my $principal = RT::Principal->new($self->CurrentUser);
705     if ($args{'PrincipalId'}) {
706         $principal->Load($args{'PrincipalId'});
707     }
708     elsif ($args{'Email'}) {
709
710         my $user = RT::User->new($self->CurrentUser);
711         $user->LoadByEmail($args{'Email'});
712
713         unless ($user->Id) {
714             $user->Load($args{'Email'});
715         }
716         if ($user->Id) { # If the user exists
717             $principal->Load($user->PrincipalId);
718         } else {
719
720         # if the user doesn't exist, we need to create a new user
721              my $new_user = RT::User->new($RT::SystemUser);
722
723             my ( $Address, $Name ) =  
724                RT::Interface::Email::ParseAddressFromHeader($args{'Email'});
725
726             my ( $Val, $Message ) = $new_user->Create(
727                 Name => $Address,
728                 EmailAddress => $Address,
729                 RealName     => $Name,
730                 Privileged   => 0,
731                 Comments     => 'Autocreated when added as a watcher');
732             unless ($Val) {
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'});
736             }
737             $principal->Load($new_user->PrincipalId);
738         }
739     }
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"));
743     }
744
745
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"));
750     }
751
752     if ( $group->HasMember( $principal)) {
753
754         return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
755     }
756
757
758     my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
759     unless ($m_id) {
760         $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
761
762         return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
763     }
764     return ( 1, $self->loc('Added principal as a [_1] for this queue', $args{'Type'}) );
765 }
766
767 # }}}
768
769 # {{{ sub DeleteWatcher
770
771 =head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
772
773
774 Deletes a queue  watcher.  Takes two arguments:
775
776 Type  (one of Requestor,Cc,AdminCc)
777
778 and one of
779
780 PrincipalId (an RT::Principal Id of the watcher you want to remove)
781     OR
782 Email (the email address of an existing wathcer)
783
784
785 =cut
786
787
788 sub DeleteWatcher {
789     my $self = shift;
790
791     my %args = ( Type => undef,
792                  PrincipalId => undef,
793                  @_ );
794
795     unless ($args{'PrincipalId'} ) {
796         return(0, $self->loc("No principal specified"));
797     }
798     my $principal = RT::Principal->new($self->CurrentUser);
799     $principal->Load($args{'PrincipalId'});
800
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"));
804     }
805
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"));
810     }
811
812     # {{{ Check ACLS
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'))
821             }
822         }
823
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'))
830             }
831         }
832         else {
833             $RT::Logger->warning( "$self -> DeleteWatcher got passed a bogus type");
834             return ( 0, $self->loc('Error in parameters to Queue->DeleteWatcher') );
835         }
836     }
837
838     # If the watcher isn't the current user 
839     # and the current user  doesn't have 'ModifyQueueWathcers' bail
840     else {
841         unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
842             return ( 0, $self->loc("Permission Denied") );
843         }
844     }
845
846     # }}}
847
848
849     # see if this user is already a watcher.
850
851     unless ( $group->HasMember($principal)) {
852         return ( 0,
853         $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
854     }
855
856     my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
857     unless ($m_id) {
858         $RT::Logger->error("Failed to delete ".$principal->Id.
859                            " as a member of group ".$group->Id."\n".$m_msg);
860
861         return ( 0,    $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
862     }
863
864     return ( 1, $self->loc("[_1] is no longer a [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
865 }
866
867 # }}}
868
869 # {{{ AdminCcAddresses
870
871 =head2 AdminCcAddresses
872
873 returns String: All queue AdminCc email addresses as a string
874
875 =cut
876
877 sub AdminCcAddresses {
878     my $self = shift;
879     
880     unless ( $self->CurrentUserHasRight('SeeQueue') ) {
881         return undef;
882     }   
883     
884     return ( $self->AdminCc->MemberEmailAddressesAsString )
885     
886 }   
887
888 # }}}
889
890 # {{{ CcAddresses
891
892 =head2 CcAddresses
893
894 returns String: All queue Ccs as a string of email addresses
895
896 =cut
897
898 sub CcAddresses {
899     my $self = shift;
900
901     unless ( $self->CurrentUserHasRight('SeeQueue') ) {
902         return undef;
903     }
904
905     return ( $self->Cc->MemberEmailAddressesAsString);
906
907 }
908 # }}}
909
910
911 # {{{ sub Cc
912
913 =head2 Cc
914
915 Takes nothing.
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
918
919 =cut
920
921 sub Cc {
922     my $self = shift;
923
924     my $group = RT::Group->new($self->CurrentUser);
925     if ( $self->CurrentUserHasRight('SeeQueue') ) {
926         $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
927     }
928     return ($group);
929
930 }
931
932 # }}}
933
934 # {{{ sub AdminCc
935
936 =head2 AdminCc
937
938 Takes nothing.
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
941
942 =cut
943
944 sub AdminCc {
945     my $self = shift;
946
947     my $group = RT::Group->new($self->CurrentUser);
948     if ( $self->CurrentUserHasRight('SeeQueue') ) {
949         $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
950     }
951     return ($group);
952
953 }
954
955 # }}}
956
957 # {{{ IsWatcher, IsCc, IsAdminCc
958
959 # {{{ sub IsWatcher
960 # a generic routine to be called by IsRequestor, IsCc and IsAdminCc
961
962 =head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
963
964 Takes a param hash with the attributes Type and PrincipalId
965
966 Type is one of Requestor, Cc, AdminCc and Owner
967
968 PrincipalId is an RT::Principal id 
969
970 Returns true if that principal is a member of the group Type for this queue
971
972
973 =cut
974
975 sub IsWatcher {
976     my $self = shift;
977
978     my %args = ( Type  => 'Cc',
979         PrincipalId    => undef,
980         @_
981     );
982
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
987
988     my $principal = RT::Principal->new($self->CurrentUser);
989     $principal->Load($args{'PrincipalId'});
990     unless ($principal->Id) {
991         return (undef);
992     }
993
994     return ($group->HasMemberRecursively($principal));
995 }
996
997 # }}}
998
999
1000 # {{{ sub IsCc
1001
1002 =head2 IsCc PRINCIPAL_ID
1003
1004 Takes an RT::Principal id.
1005 Returns true if the principal is a requestor of the current queue.
1006
1007
1008 =cut
1009
1010 sub IsCc {
1011     my $self = shift;
1012     my $cc   = shift;
1013
1014     return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
1015
1016 }
1017
1018 # }}}
1019
1020 # {{{ sub IsAdminCc
1021
1022 =head2 IsAdminCc PRINCIPAL_ID
1023
1024 Takes an RT::Principal id.
1025 Returns true if the principal is a requestor of the current queue.
1026
1027 =cut
1028
1029 sub IsAdminCc {
1030     my $self   = shift;
1031     my $person = shift;
1032
1033     return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
1034
1035 }
1036
1037 # }}}
1038
1039
1040 # }}}
1041
1042
1043
1044
1045
1046 # }}}
1047
1048 # {{{ ACCESS CONTROL
1049
1050 # {{{ sub _Set
1051 sub _Set {
1052     my $self = shift;
1053
1054     unless ( $self->CurrentUserHasRight('AdminQueue') ) {
1055         return ( 0, $self->loc('Permission Denied') );
1056     }
1057     return ( $self->SUPER::_Set(@_) );
1058 }
1059
1060 # }}}
1061
1062 # {{{ sub _Value
1063
1064 sub _Value {
1065     my $self = shift;
1066
1067     unless ( $self->CurrentUserHasRight('SeeQueue') ) {
1068         return (undef);
1069     }
1070
1071     return ( $self->__Value(@_) );
1072 }
1073
1074 # }}}
1075
1076 # {{{ sub CurrentUserHasRight
1077
1078 =head2 CurrentUserHasRight
1079
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.
1083
1084 =cut
1085
1086 sub CurrentUserHasRight {
1087     my $self  = shift;
1088     my $right = shift;
1089
1090     return (
1091         $self->HasRight(
1092             Principal => $self->CurrentUser,
1093             Right     => "$right"
1094           )
1095     );
1096
1097 }
1098
1099 # }}}
1100
1101 # {{{ sub HasRight
1102
1103 =head2 HasRight
1104
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.
1109
1110 =cut
1111
1112 # TAKES: Right and optional "Principal" which defaults to the current user
1113 sub HasRight {
1114     my $self = shift;
1115     my %args = (
1116         Right     => undef,
1117         Principal => $self->CurrentUser,
1118         @_
1119     );
1120     unless ( defined $args{'Principal'} ) {
1121         $RT::Logger->debug("Principal undefined in Queue::HasRight");
1122
1123     }
1124     return (
1125         $args{'Principal'}->HasRight(
1126             Object => $self->Id ? $self : $RT::System,
1127             Right    => $args{'Right'}
1128           )
1129     );
1130 }
1131
1132 # }}}
1133
1134 # }}}
1135
1136 1;