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