import rt 3.6.10
[freeside.git] / rt / lib / RT / Queue_Overlay.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2
3 # COPYRIGHT:
4 #  
5 # This software is Copyright (c) 1996-2009 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/licenses/old-licenses/gpl-2.0.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     return ( 0, "No principal specified" )
652         unless $args{'Email'} or $args{'PrincipalId'};
653
654     if ( !$args{'PrincipalId'} && $args{'Email'} ) {
655         my $user = RT::User->new( $self->CurrentUser );
656         $user->LoadByEmail( $args{'Email'} );
657         $args{'PrincipalId'} = $user->PrincipalId if $user->id;
658     }
659
660     # {{{ Check ACLS
661     return ( $self->_AddWatcher(%args) )
662         if $self->CurrentUserHasRight('ModifyQueueWatchers');
663
664     #If the watcher we're trying to add is for the current user
665     if ( $self->CurrentUser->PrincipalId == ($args{'PrincipalId'}||0) ) {
666         #  If it's an AdminCc and they don't have 
667         #   'WatchAsAdminCc' or 'ModifyTicket', bail
668         if ( $args{'Type'} eq 'AdminCc' ) {
669             return ( $self->_AddWatcher(%args) )
670                 if $self->CurrentUserHasRight('WatchAsAdminCc');
671         }
672
673         #  If it's a Requestor or Cc and they don't have
674         #   'Watch' or 'ModifyTicket', bail
675         elsif ( $args{'Type'} eq 'Cc' or $args{'Type'} eq 'Requestor' ) {
676             return ( $self->_AddWatcher(%args) )
677                 if $self->CurrentUserHasRight('Watch');
678         }
679         else {
680             $RT::Logger->warning( "$self -> AddWatcher got passed a bogus type");
681             return ( 0, $self->loc('Error in parameters to Queue->AddWatcher') );
682         }
683     }
684
685     return ( 0, $self->loc("Permission Denied") );
686 }
687
688 #This contains the meat of AddWatcher. but can be called from a routine like
689 # Create, which doesn't need the additional acl check
690 sub _AddWatcher {
691     my $self = shift;
692     my %args = (
693         Type   => undef,
694         Silent => undef,
695         PrincipalId => undef,
696         Email => undef,
697         @_
698     );
699
700
701     my $principal = RT::Principal->new( $self->CurrentUser );
702     if ( $args{'PrincipalId'} ) {
703         $principal->Load( $args{'PrincipalId'} );
704     }
705     elsif ( $args{'Email'} ) {
706         my $user = RT::User->new($self->CurrentUser);
707         $user->LoadByEmail( $args{'Email'} );
708         $user->Load( $args{'Email'} )
709             unless $user->id;
710
711         if ( $user->Id ) { # If the user exists
712             $principal->Load( $user->PrincipalId );
713         } else {
714             # if the user doesn't exist, we need to create a new user
715             my $new_user = RT::User->new($RT::SystemUser);
716
717             my ( $Address, $Name ) =  
718                RT::Interface::Email::ParseAddressFromHeader($args{'Email'});
719
720             my ( $Val, $Message ) = $new_user->Create(
721                 Name         => $Address,
722                 EmailAddress => $Address,
723                 RealName     => $Name,
724                 Privileged   => 0,
725                 Comments     => 'Autocreated when added as a watcher'
726             );
727             unless ($Val) {
728                 $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message);
729                 # Deal with the race condition of two account creations at once
730                 $new_user->LoadByEmail( $args{'Email'} );
731             }
732             $principal->Load( $new_user->PrincipalId );
733         }
734     }
735     # If we can't find this watcher, we need to bail.
736     unless ( $principal->Id ) {
737         return(0, $self->loc("Could not find or create that user"));
738     }
739
740     my $group = RT::Group->new($self->CurrentUser);
741     $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
742     unless ($group->id) {
743         return(0,$self->loc("Group not found"));
744     }
745
746     if ( $group->HasMember( $principal)) {
747
748         return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
749     }
750
751
752     my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
753     unless ($m_id) {
754         $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
755
756         return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
757     }
758     return ( 1, $self->loc('Added principal as a [_1] for this queue', $args{'Type'}) );
759 }
760
761 # }}}
762
763 # {{{ sub DeleteWatcher
764
765 =head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
766
767
768 Deletes a queue  watcher.  Takes two arguments:
769
770 Type  (one of Requestor,Cc,AdminCc)
771
772 and one of
773
774 PrincipalId (an RT::Principal Id of the watcher you want to remove)
775     OR
776 Email (the email address of an existing wathcer)
777
778
779 =cut
780
781
782 sub DeleteWatcher {
783     my $self = shift;
784
785     my %args = ( Type => undef,
786                  PrincipalId => undef,
787                  Email => undef,
788                  @_ );
789
790     return ( 0, "No principal specified" )
791         unless $args{Email} or $args{PrincipalId};
792
793     if ( !$args{PrincipalId} and $args{Email} ) {
794         my $user = RT::User->new( $self->CurrentUser );
795         my ($rv, $msg) = $user->LoadByEmail( $args{Email} );
796         $args{PrincipalId} = $user->PrincipalId if $rv;
797     }
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     my $can_modify_queue = $self->CurrentUserHasRight('ModifyQueueWatchers');
814
815     # {{{ Check ACLS
816     #If the watcher we're trying to add is for the current user
817     if ( $self->CurrentUser->PrincipalId  eq $args{'PrincipalId'}) {
818         #  If it's an AdminCc and they don't have 
819         #   'WatchAsAdminCc' or 'ModifyQueue', bail
820         if ( $args{'Type'} eq 'AdminCc' ) {
821             unless ( $can_modify_queue
822                 or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
823                 return ( 0, $self->loc('Permission Denied'))
824             }
825         }
826
827         #  If it's a Requestor or Cc and they don't have
828         #   'Watch' or 'ModifyQueue', bail
829         elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
830             unless ( $can_modify_queue
831                 or $self->CurrentUserHasRight('Watch') ) {
832                 return ( 0, $self->loc('Permission Denied'))
833             }
834         }
835         else {
836             $RT::Logger->warning( "$self -> DeleteWatcher got passed a bogus type");
837             return ( 0, $self->loc('Error in parameters to Queue->DeleteWatcher') );
838         }
839     }
840
841     # If the watcher isn't the current user 
842     # and the current user  doesn't have 'ModifyQueueWathcers' bail
843     else {
844         unless ( $can_modify_queue ) {
845             return ( 0, $self->loc("Permission Denied") );
846         }
847     }
848
849     # }}}
850
851
852     # see if this user is already a watcher.
853
854     unless ( $group->HasMember($principal)) {
855         return ( 0,
856         $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
857     }
858
859     my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
860     unless ($m_id) {
861         $RT::Logger->error("Failed to delete ".$principal->Id.
862                            " as a member of group ".$group->Id."\n".$m_msg);
863
864         return ( 0,    $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
865     }
866
867     return ( 1, $self->loc("[_1] is no longer a [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
868 }
869
870 # }}}
871
872 # {{{ AdminCcAddresses
873
874 =head2 AdminCcAddresses
875
876 returns String: All queue AdminCc email addresses as a string
877
878 =cut
879
880 sub AdminCcAddresses {
881     my $self = shift;
882     
883     unless ( $self->CurrentUserHasRight('SeeQueue') ) {
884         return undef;
885     }   
886     
887     return ( $self->AdminCc->MemberEmailAddressesAsString )
888     
889 }   
890
891 # }}}
892
893 # {{{ CcAddresses
894
895 =head2 CcAddresses
896
897 returns String: All queue Ccs as a string of email addresses
898
899 =cut
900
901 sub CcAddresses {
902     my $self = shift;
903
904     unless ( $self->CurrentUserHasRight('SeeQueue') ) {
905         return undef;
906     }
907
908     return ( $self->Cc->MemberEmailAddressesAsString);
909
910 }
911 # }}}
912
913
914 # {{{ sub Cc
915
916 =head2 Cc
917
918 Takes nothing.
919 Returns an RT::Group object which contains this Queue's Ccs.
920 If the user doesn't have "ShowQueue" permission, returns an empty group
921
922 =cut
923
924 sub Cc {
925     my $self = shift;
926
927     my $group = RT::Group->new($self->CurrentUser);
928     if ( $self->CurrentUserHasRight('SeeQueue') ) {
929         $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
930     }
931     return ($group);
932
933 }
934
935 # }}}
936
937 # {{{ sub AdminCc
938
939 =head2 AdminCc
940
941 Takes nothing.
942 Returns an RT::Group object which contains this Queue's AdminCcs.
943 If the user doesn't have "ShowQueue" permission, returns an empty group
944
945 =cut
946
947 sub AdminCc {
948     my $self = shift;
949
950     my $group = RT::Group->new($self->CurrentUser);
951     if ( $self->CurrentUserHasRight('SeeQueue') ) {
952         $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
953     }
954     return ($group);
955
956 }
957
958 # }}}
959
960 # {{{ IsWatcher, IsCc, IsAdminCc
961
962 # {{{ sub IsWatcher
963 # a generic routine to be called by IsRequestor, IsCc and IsAdminCc
964
965 =head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
966
967 Takes a param hash with the attributes Type and PrincipalId
968
969 Type is one of Requestor, Cc, AdminCc and Owner
970
971 PrincipalId is an RT::Principal id 
972
973 Returns true if that principal is a member of the group Type for this queue
974
975
976 =cut
977
978 sub IsWatcher {
979     my $self = shift;
980
981     my %args = ( Type  => 'Cc',
982         PrincipalId    => undef,
983         @_
984     );
985
986     # Load the relevant group. 
987     my $group = RT::Group->new($self->CurrentUser);
988     $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->id);
989     # Ask if it has the member in question
990
991     my $principal = RT::Principal->new($self->CurrentUser);
992     $principal->Load($args{'PrincipalId'});
993     unless ($principal->Id) {
994         return (undef);
995     }
996
997     return ($group->HasMemberRecursively($principal));
998 }
999
1000 # }}}
1001
1002
1003 # {{{ sub IsCc
1004
1005 =head2 IsCc PRINCIPAL_ID
1006
1007 Takes an RT::Principal id.
1008 Returns true if the principal is a requestor of the current queue.
1009
1010
1011 =cut
1012
1013 sub IsCc {
1014     my $self = shift;
1015     my $cc   = shift;
1016
1017     return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
1018
1019 }
1020
1021 # }}}
1022
1023 # {{{ sub IsAdminCc
1024
1025 =head2 IsAdminCc PRINCIPAL_ID
1026
1027 Takes an RT::Principal id.
1028 Returns true if the principal is a requestor of the current queue.
1029
1030 =cut
1031
1032 sub IsAdminCc {
1033     my $self   = shift;
1034     my $person = shift;
1035
1036     return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
1037
1038 }
1039
1040 # }}}
1041
1042
1043 # }}}
1044
1045
1046
1047
1048
1049 # }}}
1050
1051 # {{{ ACCESS CONTROL
1052
1053 # {{{ sub _Set
1054 sub _Set {
1055     my $self = shift;
1056
1057     unless ( $self->CurrentUserHasRight('AdminQueue') ) {
1058         return ( 0, $self->loc('Permission Denied') );
1059     }
1060     return ( $self->SUPER::_Set(@_) );
1061 }
1062
1063 # }}}
1064
1065 # {{{ sub _Value
1066
1067 sub _Value {
1068     my $self = shift;
1069
1070     unless ( $self->CurrentUserHasRight('SeeQueue') ) {
1071         return (undef);
1072     }
1073
1074     return ( $self->__Value(@_) );
1075 }
1076
1077 # }}}
1078
1079 # {{{ sub CurrentUserHasRight
1080
1081 =head2 CurrentUserHasRight
1082
1083 Takes one argument. A textual string with the name of the right we want to check.
1084 Returns true if the current user has that right for this queue.
1085 Returns undef otherwise.
1086
1087 =cut
1088
1089 sub CurrentUserHasRight {
1090     my $self  = shift;
1091     my $right = shift;
1092
1093     return (
1094         $self->HasRight(
1095             Principal => $self->CurrentUser,
1096             Right     => "$right"
1097           )
1098     );
1099
1100 }
1101
1102 # }}}
1103
1104 # {{{ sub HasRight
1105
1106 =head2 HasRight
1107
1108 Takes a param hash with the fields 'Right' and 'Principal'.
1109 Principal defaults to the current user.
1110 Returns true if the principal has that right for this queue.
1111 Returns undef otherwise.
1112
1113 =cut
1114
1115 # TAKES: Right and optional "Principal" which defaults to the current user
1116 sub HasRight {
1117     my $self = shift;
1118     my %args = (
1119         Right     => undef,
1120         Principal => $self->CurrentUser,
1121         @_
1122     );
1123     unless ( defined $args{'Principal'} ) {
1124         $RT::Logger->debug("Principal undefined in Queue::HasRight");
1125
1126     }
1127     return (
1128         $args{'Principal'}->HasRight(
1129             Object => $self->Id ? $self : $RT::System,
1130             Right    => $args{'Right'}
1131           )
1132     );
1133 }
1134
1135 # }}}
1136
1137 # }}}
1138
1139 1;