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