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