]> git.uio.no Git - usit-rt.git/blame - lib/RT/Queue.pm
Upgrade to 4.0.10.
[usit-rt.git] / lib / RT / Queue.pm
CommitLineData
84fb5b46
MKG
1# BEGIN BPS TAGGED BLOCK {{{
2#
3# COPYRIGHT:
4#
403d7b0b 5# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
84fb5b46
MKG
6# <sales@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
49=head1 NAME
50
51 RT::Queue - an RT Queue object
52
53=head1 SYNOPSIS
54
55 use RT::Queue;
56
57=head1 DESCRIPTION
58
59An RT queue object.
60
61=head1 METHODS
62
63=cut
64
65
66package RT::Queue;
67
68use strict;
69use warnings;
70use base 'RT::Record';
71
72sub Table {'Queues'}
73
74
75
76use RT::Groups;
77use RT::ACL;
78use RT::Interface::Email;
79
80our @DEFAULT_ACTIVE_STATUS = qw(new open stalled);
81our @DEFAULT_INACTIVE_STATUS = qw(resolved rejected deleted);
82
83# $self->loc('new'); # For the string extractor to get a string to localize
84# $self->loc('open'); # For the string extractor to get a string to localize
85# $self->loc('stalled'); # For the string extractor to get a string to localize
86# $self->loc('resolved'); # For the string extractor to get a string to localize
87# $self->loc('rejected'); # For the string extractor to get a string to localize
88# $self->loc('deleted'); # For the string extractor to get a string to localize
89
90
91our $RIGHTS = {
92 SeeQueue => 'View queue', # loc_pair
93 AdminQueue => 'Create, modify and delete queue', # loc_pair
94 ShowACL => 'Display Access Control List', # loc_pair
95 ModifyACL => 'Create, modify and delete Access Control List entries', # loc_pair
96 ModifyQueueWatchers => 'Modify queue watchers', # loc_pair
97 SeeCustomField => 'View custom field values', # loc_pair
98 ModifyCustomField => 'Modify custom field values', # loc_pair
99 AssignCustomFields => 'Assign and remove queue custom fields', # loc_pair
100 ModifyTemplate => 'Modify Scrip templates', # loc_pair
101 ShowTemplate => 'View Scrip templates', # loc_pair
102
103 ModifyScrips => 'Modify Scrips', # loc_pair
104 ShowScrips => 'View Scrips', # loc_pair
105
106 ShowTicket => 'View ticket summaries', # loc_pair
107 ShowTicketComments => 'View ticket private commentary', # loc_pair
108 ShowOutgoingEmail => 'View exact outgoing email messages and their recipients', # loc_pair
109
110 Watch => 'Sign up as a ticket Requestor or ticket or queue Cc', # loc_pair
111 WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc', # loc_pair
112 CreateTicket => 'Create tickets', # loc_pair
113 ReplyToTicket => 'Reply to tickets', # loc_pair
114 CommentOnTicket => 'Comment on tickets', # loc_pair
115 OwnTicket => 'Own tickets', # loc_pair
116 ModifyTicket => 'Modify tickets', # loc_pair
117 DeleteTicket => 'Delete tickets', # loc_pair
118 TakeTicket => 'Take tickets', # loc_pair
119 StealTicket => 'Steal tickets', # loc_pair
120
121 ForwardMessage => 'Forward messages outside of RT', # loc_pair
122};
123
124our $RIGHT_CATEGORIES = {
125 SeeQueue => 'General',
126 AdminQueue => 'Admin',
127 ShowACL => 'Admin',
128 ModifyACL => 'Admin',
129 ModifyQueueWatchers => 'Admin',
130 SeeCustomField => 'General',
131 ModifyCustomField => 'Staff',
132 AssignCustomFields => 'Admin',
133 ModifyTemplate => 'Admin',
134 ShowTemplate => 'Admin',
135 ModifyScrips => 'Admin',
136 ShowScrips => 'Admin',
137 ShowTicket => 'General',
138 ShowTicketComments => 'Staff',
139 ShowOutgoingEmail => 'Staff',
140 Watch => 'General',
141 WatchAsAdminCc => 'Staff',
142 CreateTicket => 'General',
143 ReplyToTicket => 'General',
144 CommentOnTicket => 'General',
145 OwnTicket => 'Staff',
146 ModifyTicket => 'Staff',
147 DeleteTicket => 'Staff',
148 TakeTicket => 'Staff',
149 StealTicket => 'Staff',
150 ForwardMessage => 'Staff',
151};
152
153# Tell RT::ACE that this sort of object can get acls granted
154$RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
155
156# TODO: This should be refactored out into an RT::ACLedObject or something
157# stuff the rights into a hash of rights that can exist.
158
159__PACKAGE__->AddRights(%$RIGHTS);
160__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
161require RT::Lifecycle;
162
163=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
164
165Adds the given rights to the list of possible rights. This method
166should be called during server startup, not at runtime.
167
168=cut
169
170sub AddRights {
171 my $self = shift;
172 my %new = @_;
173 $RIGHTS = { %$RIGHTS, %new };
174 %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
175 map { lc($_) => $_ } keys %new);
176}
177
178=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
179
180Adds the given right and category pairs to the list of right categories. This
181method should be called during server startup, not at runtime.
182
183=cut
184
185sub AddRightCategories {
186 my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
187 my %new = @_;
188 $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
189}
190
191sub AddLink {
192 my $self = shift;
193 my %args = ( Target => '',
194 Base => '',
195 Type => '',
196 Silent => undef,
197 @_ );
198
199 unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
200 return ( 0, $self->loc("Permission Denied") );
201 }
202
203 return $self->SUPER::_AddLink(%args);
204}
205
206sub DeleteLink {
207 my $self = shift;
208 my %args = (
209 Base => undef,
210 Target => undef,
211 Type => undef,
212 @_
213 );
214
215 #check acls
216 unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
217 $RT::Logger->debug("No permission to delete links");
218 return ( 0, $self->loc('Permission Denied'))
219 }
220
221 return $self->SUPER::_DeleteLink(%args);
222}
223
224=head2 AvailableRights
225
226Returns 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
227
228=cut
229
230sub AvailableRights {
231 my $self = shift;
232 return($RIGHTS);
233}
234
235=head2 RightCategories
236
237Returns a hashref where the keys are rights for this type of object and the
238values are the category (General, Staff, Admin) the right falls into.
239
240=cut
241
242sub RightCategories {
243 return $RIGHT_CATEGORIES;
244}
245
246
247sub Lifecycle {
248 my $self = shift;
249 unless (ref $self && $self->id) {
250 return RT::Lifecycle->Load('')
251 }
252
253 my $name = $self->_Value( Lifecycle => @_ );
254 $name ||= 'default';
255
256 my $res = RT::Lifecycle->Load( $name );
257 unless ( $res ) {
258 $RT::Logger->error("Lifecycle '$name' for queue '".$self->Name."' doesn't exist");
259 return RT::Lifecycle->Load('default');
260 }
261 return $res;
262}
263
264sub SetLifecycle {
265 my $self = shift;
403d7b0b 266 my $value = shift || 'default';
84fb5b46 267
403d7b0b
MKG
268 return ( 0, $self->loc( '[_1] is not a valid lifecycle', $value ) )
269 unless $self->ValidateLifecycle($value);
84fb5b46
MKG
270
271 return $self->_Set( Field => 'Lifecycle', Value => $value, @_ );
272}
273
274=head2 ValidateLifecycle NAME
275
276Takes a lifecycle name. Returns true if it's an ok name and such
277lifecycle is configured. Returns undef otherwise.
278
279=cut
280
281sub ValidateLifecycle {
282 my $self = shift;
283 my $value = shift;
284 return undef unless RT::Lifecycle->Load( $value );
285 return 1;
286}
287
288
289=head2 ActiveStatusArray
290
291Returns an array of all ActiveStatuses for this queue
292
293=cut
294
295sub ActiveStatusArray {
296 my $self = shift;
297 return $self->Lifecycle->Valid('initial', 'active');
298}
299
300=head2 InactiveStatusArray
301
302Returns an array of all InactiveStatuses for this queue
303
304=cut
305
306sub InactiveStatusArray {
307 my $self = shift;
308 return $self->Lifecycle->Inactive;
309}
310
311=head2 StatusArray
312
313Returns an array of all statuses for this queue
314
315=cut
316
317sub StatusArray {
318 my $self = shift;
319 return $self->Lifecycle->Valid( @_ );
320}
321
322=head2 IsValidStatus value
323
324Returns true if value is a valid status. Otherwise, returns 0.
325
326=cut
327
328sub IsValidStatus {
329 my $self = shift;
330 return $self->Lifecycle->IsValid( shift );
331}
332
333=head2 IsActiveStatus value
334
335Returns true if value is a Active status. Otherwise, returns 0
336
337=cut
338
339sub IsActiveStatus {
340 my $self = shift;
341 return $self->Lifecycle->IsValid( shift, 'initial', 'active');
342}
343
344
345
346=head2 IsInactiveStatus value
347
348Returns true if value is a Inactive status. Otherwise, returns 0
349
350
351=cut
352
353sub IsInactiveStatus {
354 my $self = shift;
355 return $self->Lifecycle->IsInactive( shift );
356}
357
358
359
360
361
362
363=head2 Create(ARGS)
364
365Arguments: ARGS is a hash of named parameters. Valid parameters are:
366
367 Name (required)
368 Description
369 CorrespondAddress
370 CommentAddress
371 InitialPriority
372 FinalPriority
373 DefaultDueIn
374
375If you pass the ACL check, it creates the queue and returns its queue id.
376
377
378=cut
379
380sub Create {
381 my $self = shift;
382 my %args = (
383 Name => undef,
384 Description => '',
385 CorrespondAddress => '',
386 CommentAddress => '',
387 Lifecycle => 'default',
388 SubjectTag => undef,
389 InitialPriority => 0,
390 FinalPriority => 0,
391 DefaultDueIn => 0,
392 Sign => undef,
dab09ea8 393 SignAuto => undef,
84fb5b46
MKG
394 Encrypt => undef,
395 _RecordTransaction => 1,
396 @_
397 );
398
399 unless ( $self->CurrentUser->HasRight(Right => 'AdminQueue', Object => $RT::System) )
400 { #Check them ACLs
401 return ( 0, $self->loc("No permission to create queues") );
402 }
403
404 {
405 my ($val, $msg) = $self->_ValidateName( $args{'Name'} );
406 return ($val, $msg) unless $val;
407 }
408
403d7b0b
MKG
409 $args{'Lifecycle'} ||= 'default';
410
411 return ( 0, $self->loc('[_1] is not a valid lifecycle', $args{'Lifecycle'} ) )
412 unless $self->ValidateLifecycle( $args{'Lifecycle'} );
84fb5b46
MKG
413
414 my %attrs = map {$_ => 1} $self->ReadableAttributes;
415
416 #TODO better input validation
417 $RT::Handle->BeginTransaction();
418 my $id = $self->SUPER::Create( map { $_ => $args{$_} } grep exists $args{$_}, keys %attrs );
419 unless ($id) {
420 $RT::Handle->Rollback();
421 return ( 0, $self->loc('Queue could not be created') );
422 }
423
424 my $create_ret = $self->_CreateQueueGroups();
425 unless ($create_ret) {
426 $RT::Handle->Rollback();
427 return ( 0, $self->loc('Queue could not be created') );
428 }
429 if ( $args{'_RecordTransaction'} ) {
430 $self->_NewTransaction( Type => "Create" );
431 }
432 $RT::Handle->Commit;
433
dab09ea8
MKG
434 for my $attr (qw/Sign SignAuto Encrypt/) {
435 next unless defined $args{$attr};
436 my $set = "Set" . $attr;
437 my ($status, $msg) = $self->$set( $args{$attr} );
438 $RT::Logger->error("Couldn't set attribute '$attr': $msg")
84fb5b46
MKG
439 unless $status;
440 }
441
442 RT->System->QueueCacheNeedsUpdate(1);
443
444 return ( $id, $self->loc("Queue created") );
445}
446
447
448
449sub Delete {
450 my $self = shift;
451 return ( 0,
452 $self->loc('Deleting this object would break referential integrity') );
453}
454
455
456
457=head2 SetDisabled
458
459Takes a boolean.
4601 will cause this queue to no longer be available for tickets.
4610 will re-enable this queue.
462
463=cut
464
465sub SetDisabled {
466 my $self = shift;
467 my $val = shift;
468
469 $RT::Handle->BeginTransaction();
470 my $set_err = $self->_Set( Field =>'Disabled', Value => $val);
471 unless ($set_err) {
472 $RT::Handle->Rollback();
473 $RT::Logger->warning("Couldn't ".($val == 1) ? "disable" : "enable"." queue ".$self->PrincipalObj->Id);
474 return (undef);
475 }
476 $self->_NewTransaction( Type => ($val == 1) ? "Disabled" : "Enabled" );
477
478 $RT::Handle->Commit();
479
480 RT->System->QueueCacheNeedsUpdate(1);
481
482 if ( $val == 1 ) {
483 return (1, $self->loc("Queue disabled"));
484 } else {
485 return (1, $self->loc("Queue enabled"));
486 }
487
488}
489
490
491
492=head2 Load
493
494Takes either a numerical id or a textual Name and loads the specified queue.
495
496=cut
497
498sub Load {
499 my $self = shift;
500
501 my $identifier = shift;
502 if ( !$identifier ) {
503 return (undef);
504 }
505
506 if ( $identifier =~ /^(\d+)$/ ) {
507 $self->SUPER::LoadById($identifier);
508 }
509 else {
510 $self->LoadByCols( Name => $identifier );
511 }
512
513 return ( $self->Id );
514
515}
516
517
518
519=head2 ValidateName NAME
520
521Takes a queue name. Returns true if it's an ok name for
522a new queue. Returns undef if there's already a queue by that name.
523
524=cut
525
526sub ValidateName {
527 my $self = shift;
528 my $name = shift;
529
530 my ($ok, $msg) = $self->_ValidateName($name);
531
532 return $ok ? 1 : 0;
533}
534
535sub _ValidateName {
536 my $self = shift;
537 my $name = shift;
538
539 return (undef, "Queue name is required") unless length $name;
540
541 # Validate via the superclass first
542 # Case: short circuit if it's an integer so we don't have
543 # fale negatives when loading a temp queue
544 unless ( my $q = $self->SUPER::ValidateName($name) ) {
545 return ($q, $self->loc("'[_1]' is not a valid name.", $name));
546 }
547
548 my $tempqueue = RT::Queue->new(RT->SystemUser);
549 $tempqueue->Load($name);
550
551 #If this queue exists, return undef
552 if ( $tempqueue->Name() && $tempqueue->id != $self->id) {
553 return (undef, $self->loc("Queue already exists") );
554 }
555
556 return (1);
557}
558
559
560=head2 SetSign
561
562=cut
563
564sub Sign {
565 my $self = shift;
566 my $value = shift;
567
568 return undef unless $self->CurrentUserHasRight('SeeQueue');
569 my $attr = $self->FirstAttribute('Sign') or return 0;
570 return $attr->Content;
571}
572
573sub SetSign {
574 my $self = shift;
575 my $value = shift;
576
577 return ( 0, $self->loc('Permission Denied') )
578 unless $self->CurrentUserHasRight('AdminQueue');
579
580 my ($status, $msg) = $self->SetAttribute(
581 Name => 'Sign',
582 Description => 'Sign outgoing messages by default',
583 Content => $value,
584 );
585 return ($status, $msg) unless $status;
dab09ea8
MKG
586 return ($status, $self->loc('Signing enabled')) if $value;
587 return ($status, $self->loc('Signing disabled'));
588}
589
590sub SignAuto {
591 my $self = shift;
592 my $value = shift;
593
594 return undef unless $self->CurrentUserHasRight('SeeQueue');
595 my $attr = $self->FirstAttribute('SignAuto') or return 0;
596 return $attr->Content;
597}
598
599sub SetSignAuto {
600 my $self = shift;
601 my $value = shift;
602
603 return ( 0, $self->loc('Permission Denied') )
604 unless $self->CurrentUserHasRight('AdminQueue');
605
606 my ($status, $msg) = $self->SetAttribute(
607 Name => 'SignAuto',
608 Description => 'Sign auto-generated outgoing messages',
609 Content => $value,
610 );
611 return ($status, $msg) unless $status;
84fb5b46
MKG
612 return ($status, $self->loc('Signing enabled')) if $value;
613 return ($status, $self->loc('Signing disabled'));
614}
615
616sub Encrypt {
617 my $self = shift;
618 my $value = shift;
619
620 return undef unless $self->CurrentUserHasRight('SeeQueue');
621 my $attr = $self->FirstAttribute('Encrypt') or return 0;
622 return $attr->Content;
623}
624
625sub SetEncrypt {
626 my $self = shift;
627 my $value = shift;
628
629 return ( 0, $self->loc('Permission Denied') )
630 unless $self->CurrentUserHasRight('AdminQueue');
631
632 my ($status, $msg) = $self->SetAttribute(
633 Name => 'Encrypt',
634 Description => 'Encrypt outgoing messages by default',
635 Content => $value,
636 );
637 return ($status, $msg) unless $status;
638 return ($status, $self->loc('Encrypting enabled')) if $value;
639 return ($status, $self->loc('Encrypting disabled'));
640}
641
642=head2 Templates
643
644Returns an RT::Templates object of all of this queue's templates.
645
646=cut
647
648sub Templates {
649 my $self = shift;
650
651 my $templates = RT::Templates->new( $self->CurrentUser );
652
653 if ( $self->CurrentUserHasRight('ShowTemplate') ) {
654 $templates->LimitToQueue( $self->id );
655 }
656
657 return ($templates);
658}
659
660
661
662
663=head2 CustomField NAME
664
665Load the queue-specific custom field named NAME
666
667=cut
668
669sub CustomField {
670 my $self = shift;
671 my $name = shift;
672 my $cf = RT::CustomField->new($self->CurrentUser);
673 $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id);
674 return ($cf);
675}
676
677
678
679=head2 TicketCustomFields
680
681Returns an L<RT::CustomFields> object containing all global and
682queue-specific B<ticket> custom fields.
683
684=cut
685
686sub TicketCustomFields {
687 my $self = shift;
688
689 my $cfs = RT::CustomFields->new( $self->CurrentUser );
690 if ( $self->CurrentUserHasRight('SeeQueue') ) {
691 $cfs->SetContextObject( $self );
692 $cfs->LimitToGlobalOrObjectId( $self->Id );
693 $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket' );
694 $cfs->ApplySortOrder;
695 }
696 return ($cfs);
697}
698
699
700
701=head2 TicketTransactionCustomFields
702
703Returns an L<RT::CustomFields> object containing all global and
704queue-specific B<transaction> custom fields.
705
706=cut
707
708sub TicketTransactionCustomFields {
709 my $self = shift;
710
711 my $cfs = RT::CustomFields->new( $self->CurrentUser );
712 if ( $self->CurrentUserHasRight('SeeQueue') ) {
713 $cfs->SetContextObject( $self );
714 $cfs->LimitToGlobalOrObjectId( $self->Id );
715 $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' );
716 $cfs->ApplySortOrder;
717 }
718 return ($cfs);
719}
720
721
722
723
724
725=head2 AllRoleGroupTypes
726
727Returns a list of the names of the various role group types that this queue
728has, including Requestor and Owner. If you don't want them, see
729L</ManageableRoleGroupTypes>.
730
731=cut
732
733sub AllRoleGroupTypes {
734 my $self = shift;
735 return ($self->ManageableRoleGroupTypes, qw(Requestor Owner));
736}
737
738=head2 IsRoleGroupType
739
740Returns whether the passed-in type is a role group type.
741
742=cut
743
744sub IsRoleGroupType {
745 my $self = shift;
746 my $type = shift;
747
748 for my $valid_type ($self->AllRoleGroupTypes) {
749 return 1 if $type eq $valid_type;
750 }
751
752 return 0;
753}
754
755=head2 ManageableRoleGroupTypes
756
757Returns a list of the names of the various role group types that this queue
758has, excluding Requestor and Owner. If you want them, see L</AllRoleGroupTypes>.
759
760=cut
761
762sub ManageableRoleGroupTypes {
763 return qw(Cc AdminCc);
764}
765
766=head2 IsManageableRoleGroupType
767
768Returns whether the passed-in type is a manageable role group type.
769
770=cut
771
772sub IsManageableRoleGroupType {
773 my $self = shift;
774 my $type = shift;
775
776 for my $valid_type ($self->ManageableRoleGroupTypes) {
777 return 1 if $type eq $valid_type;
778 }
779
780 return 0;
781}
782
783
784=head2 _CreateQueueGroups
785
786Create the ticket groups and links for this ticket.
787This routine expects to be called from Ticket->Create _inside of a transaction_
788
789It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
790
791It will return true on success and undef on failure.
792
793
794=cut
795
796sub _CreateQueueGroups {
797 my $self = shift;
798
799 my @types = $self->AllRoleGroupTypes;
800
801 foreach my $type (@types) {
802 my $ok = $self->_CreateQueueRoleGroup($type);
803 return undef if !$ok;
804 }
805
806 return 1;
807}
808
809sub _CreateQueueRoleGroup {
810 my $self = shift;
811 my $type = shift;
812
813 my $type_obj = RT::Group->new($self->CurrentUser);
814 my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id,
815 Type => $type,
816 Domain => 'RT::Queue-Role');
817 unless ($id) {
818 $RT::Logger->error("Couldn't create a Queue group of type '$type' for queue ".
819 $self->Id.": ".$msg);
820 return(undef);
821 }
822
823 return $id;
824}
825
826
827
828# _HasModifyWatcherRight {{{
829sub _HasModifyWatcherRight {
830 my $self = shift;
831 my %args = (
832 Type => undef,
833 PrincipalId => undef,
834 Email => undef,
835 @_
836 );
837
838 return 1 if $self->CurrentUserHasRight('ModifyQueueWatchers');
839
840 #If the watcher we're trying to add is for the current user
841 if ( defined $args{'PrincipalId'} && $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
842 if ( $args{'Type'} eq 'AdminCc' ) {
843 return 1 if $self->CurrentUserHasRight('WatchAsAdminCc');
844 }
845 elsif ( $args{'Type'} eq 'Cc' or $args{'Type'} eq 'Requestor' ) {
846 return 1 if $self->CurrentUserHasRight('Watch');
847 }
848 else {
849 $RT::Logger->warning( "$self -> _HasModifyWatcher got passed a bogus type $args{Type}");
850 return ( 0, $self->loc('Invalid queue role group type [_1]', $args{Type}) );
851 }
852 }
853
854 return ( 0, $self->loc("Permission Denied") );
855}
856
857
858=head2 AddWatcher
859
860AddWatcher takes a parameter hash. The keys are as follows:
861
862Type One of Requestor, Cc, AdminCc
863
864PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
865Email The email address of the new watcher. If a user with this
866 email address can't be found, a new nonprivileged user will be created.
867
403d7b0b 868If the watcher you're trying to set has an RT account, set the Owner parameter to their User Id. Otherwise, set the Email parameter to their Email address.
84fb5b46
MKG
869
870Returns a tuple of (status/id, message).
871
872=cut
873
874sub AddWatcher {
875 my $self = shift;
876 my %args = (
877 Type => undef,
878 PrincipalId => undef,
879 Email => undef,
880 @_
881 );
882
883 return ( 0, "No principal specified" )
884 unless $args{'Email'} or $args{'PrincipalId'};
885
886 if ( !$args{'PrincipalId'} && $args{'Email'} ) {
887 my $user = RT::User->new( $self->CurrentUser );
888 $user->LoadByEmail( $args{'Email'} );
889 $args{'PrincipalId'} = $user->PrincipalId if $user->id;
890 }
891
892 return ( 0, "Unknown watcher type [_1]", $args{Type} )
893 unless $self->IsRoleGroupType($args{Type});
894
895 my ($ok, $msg) = $self->_HasModifyWatcherRight(%args);
896 return ($ok, $msg) if !$ok;
897
898 return $self->_AddWatcher(%args);
899}
900
901#This contains the meat of AddWatcher. but can be called from a routine like
902# Create, which doesn't need the additional acl check
903sub _AddWatcher {
904 my $self = shift;
905 my %args = (
906 Type => undef,
907 Silent => undef,
908 PrincipalId => undef,
909 Email => undef,
910 @_
911 );
912
913
914 my $principal = RT::Principal->new( $self->CurrentUser );
915 if ( $args{'PrincipalId'} ) {
916 $principal->Load( $args{'PrincipalId'} );
917 if ( $principal->id and $principal->IsUser and my $email = $principal->Object->EmailAddress ) {
918 return (0, $self->loc("[_1] is an address RT receives mail at. Adding it as a '[_2]' would create a mail loop", $email, $self->loc($args{'Type'})))
919 if RT::EmailParser->IsRTAddress( $email );
920 }
921 }
922 elsif ( $args{'Email'} ) {
923 if ( RT::EmailParser->IsRTAddress( $args{'Email'} ) ) {
924 return (0, $self->loc("[_1] is an address RT receives mail at. Adding it as a '[_2]' would create a mail loop", $args{'Email'}, $self->loc($args{'Type'})));
925 }
926 my $user = RT::User->new($self->CurrentUser);
927 $user->LoadByEmail( $args{'Email'} );
928 $user->Load( $args{'Email'} )
929 unless $user->id;
930
931 if ( $user->Id ) { # If the user exists
932 $principal->Load( $user->PrincipalId );
933 } else {
934 # if the user doesn't exist, we need to create a new user
935 my $new_user = RT::User->new(RT->SystemUser);
936
937 my ( $Address, $Name ) =
938 RT::Interface::Email::ParseAddressFromHeader($args{'Email'});
939
940 my ( $Val, $Message ) = $new_user->Create(
941 Name => $Address,
942 EmailAddress => $Address,
943 RealName => $Name,
944 Privileged => 0,
945 Comments => 'Autocreated when added as a watcher'
946 );
947 unless ($Val) {
948 $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message);
949 # Deal with the race condition of two account creations at once
950 $new_user->LoadByEmail( $args{'Email'} );
951 }
952 $principal->Load( $new_user->PrincipalId );
953 }
954 }
955 # If we can't find this watcher, we need to bail.
956 unless ( $principal->Id ) {
957 return(0, $self->loc("Could not find or create that user"));
958 }
959
960 my $group = RT::Group->new($self->CurrentUser);
961 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
962 unless ($group->id) {
963 return(0,$self->loc("Group not found"));
964 }
965
966 if ( $group->HasMember( $principal)) {
967
403d7b0b
MKG
968 return ( 0, $self->loc('[_1] is already a [_2] for this queue',
969 $principal->Object->Name, $args{'Type'}) );
84fb5b46
MKG
970 }
971
972
973 my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
974 unless ($m_id) {
975 $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg);
976
403d7b0b
MKG
977 return ( 0, $self->loc('Could not make [_1] a [_2] for this queue',
978 $principal->Object->Name, $args{'Type'}) );
84fb5b46
MKG
979 }
980 return ( 1, $self->loc("Added [_1] to members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
981}
982
983
984
985=head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
986
987
988Deletes a queue watcher. Takes two arguments:
989
990Type (one of Requestor,Cc,AdminCc)
991
992and one of
993
994PrincipalId (an RT::Principal Id of the watcher you want to remove)
995 OR
996Email (the email address of an existing wathcer)
997
998
999=cut
1000
1001
1002sub DeleteWatcher {
1003 my $self = shift;
1004
1005 my %args = ( Type => undef,
1006 PrincipalId => undef,
1007 Email => undef,
1008 @_ );
1009
1010 unless ( $args{'PrincipalId'} || $args{'Email'} ) {
1011 return ( 0, $self->loc("No principal specified") );
1012 }
1013
1014 if ( !$args{PrincipalId} and $args{Email} ) {
1015 my $user = RT::User->new( $self->CurrentUser );
1016 my ($rv, $msg) = $user->LoadByEmail( $args{Email} );
1017 $args{PrincipalId} = $user->PrincipalId if $rv;
1018 }
1019
1020 my $principal = RT::Principal->new( $self->CurrentUser );
1021 if ( $args{'PrincipalId'} ) {
1022 $principal->Load( $args{'PrincipalId'} );
1023 }
1024 else {
1025 my $user = RT::User->new( $self->CurrentUser );
1026 $user->LoadByEmail( $args{'Email'} );
1027 $principal->Load( $user->Id );
1028 }
1029
1030 # If we can't find this watcher, we need to bail.
1031 unless ( $principal->Id ) {
1032 return ( 0, $self->loc("Could not find that principal") );
1033 }
1034
1035 my $group = RT::Group->new($self->CurrentUser);
1036 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
1037 unless ($group->id) {
1038 return(0,$self->loc("Group not found"));
1039 }
1040
1041 return ( 0, $self->loc('Unknown watcher type [_1]', $args{Type}) )
1042 unless $self->IsRoleGroupType($args{Type});
1043
1044 my ($ok, $msg) = $self->_HasModifyWatcherRight(%args);
1045 return ($ok, $msg) if !$ok;
1046
1047 # see if this user is already a watcher.
1048
1049 unless ( $group->HasMember($principal)) {
403d7b0b
MKG
1050 return ( 0, $self->loc('[_1] is not a [_2] for this queue',
1051 $principal->Object->Name, $args{'Type'}) );
84fb5b46
MKG
1052 }
1053
1054 my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
1055 unless ($m_id) {
1056 $RT::Logger->error("Failed to delete ".$principal->Id.
1057 " as a member of group ".$group->Id.": ".$m_msg);
1058
403d7b0b
MKG
1059 return ( 0, $self->loc('Could not remove [_1] as a [_2] for this queue',
1060 $principal->Object->Name, $args{'Type'}) );
84fb5b46
MKG
1061 }
1062
1063 return ( 1, $self->loc("Removed [_1] from members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
1064}
1065
1066
1067
1068=head2 AdminCcAddresses
1069
1070returns String: All queue AdminCc email addresses as a string
1071
1072=cut
1073
1074sub AdminCcAddresses {
1075 my $self = shift;
1076
1077 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
1078 return undef;
1079 }
1080
1081 return ( $self->AdminCc->MemberEmailAddressesAsString )
1082
1083}
1084
1085
1086
1087=head2 CcAddresses
1088
1089returns String: All queue Ccs as a string of email addresses
1090
1091=cut
1092
1093sub CcAddresses {
1094 my $self = shift;
1095
1096 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
1097 return undef;
1098 }
1099
1100 return ( $self->Cc->MemberEmailAddressesAsString);
1101
1102}
1103
1104
1105
1106=head2 Cc
1107
1108Takes nothing.
1109Returns an RT::Group object which contains this Queue's Ccs.
1110If the user doesn't have "ShowQueue" permission, returns an empty group
1111
1112=cut
1113
1114sub Cc {
1115 my $self = shift;
1116
1117 my $group = RT::Group->new($self->CurrentUser);
1118 if ( $self->CurrentUserHasRight('SeeQueue') ) {
1119 $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
1120 }
1121 return ($group);
1122
1123}
1124
1125
1126
1127=head2 AdminCc
1128
1129Takes nothing.
1130Returns an RT::Group object which contains this Queue's AdminCcs.
1131If the user doesn't have "ShowQueue" permission, returns an empty group
1132
1133=cut
1134
1135sub AdminCc {
1136 my $self = shift;
1137
1138 my $group = RT::Group->new($self->CurrentUser);
1139 if ( $self->CurrentUserHasRight('SeeQueue') ) {
1140 $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
1141 }
1142 return ($group);
1143
1144}
1145
1146
1147
1148# a generic routine to be called by IsRequestor, IsCc and IsAdminCc
1149
1150=head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
1151
1152Takes a param hash with the attributes Type and PrincipalId
1153
1154Type is one of Requestor, Cc, AdminCc and Owner
1155
1156PrincipalId is an RT::Principal id
1157
1158Returns true if that principal is a member of the group Type for this queue
1159
1160
1161=cut
1162
1163sub IsWatcher {
1164 my $self = shift;
1165
1166 my %args = ( Type => 'Cc',
1167 PrincipalId => undef,
1168 @_
1169 );
1170
1171 # Load the relevant group.
1172 my $group = RT::Group->new($self->CurrentUser);
1173 $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->id);
1174 # Ask if it has the member in question
1175
1176 my $principal = RT::Principal->new($self->CurrentUser);
1177 $principal->Load($args{'PrincipalId'});
1178 unless ($principal->Id) {
1179 return (undef);
1180 }
1181
1182 return ($group->HasMemberRecursively($principal));
1183}
1184
1185
1186
1187
1188=head2 IsCc PRINCIPAL_ID
1189
1190Takes an RT::Principal id.
1191Returns true if the principal is a requestor of the current queue.
1192
1193
1194=cut
1195
1196sub IsCc {
1197 my $self = shift;
1198 my $cc = shift;
1199
1200 return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
1201
1202}
1203
1204
1205
1206=head2 IsAdminCc PRINCIPAL_ID
1207
1208Takes an RT::Principal id.
1209Returns true if the principal is a requestor of the current queue.
1210
1211=cut
1212
1213sub IsAdminCc {
1214 my $self = shift;
1215 my $person = shift;
1216
1217 return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
1218
1219}
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230sub _Set {
1231 my $self = shift;
1232
1233 unless ( $self->CurrentUserHasRight('AdminQueue') ) {
1234 return ( 0, $self->loc('Permission Denied') );
1235 }
403d7b0b 1236 RT->System->QueueCacheNeedsUpdate(1);
84fb5b46
MKG
1237 return ( $self->SUPER::_Set(@_) );
1238}
1239
1240
1241
1242sub _Value {
1243 my $self = shift;
1244
1245 unless ( $self->CurrentUserHasRight('SeeQueue') ) {
1246 return (undef);
1247 }
1248
1249 return ( $self->__Value(@_) );
1250}
1251
1252
1253
1254=head2 CurrentUserHasRight
1255
1256Takes one argument. A textual string with the name of the right we want to check.
1257Returns true if the current user has that right for this queue.
1258Returns undef otherwise.
1259
1260=cut
1261
1262sub CurrentUserHasRight {
1263 my $self = shift;
1264 my $right = shift;
1265
1266 return (
1267 $self->HasRight(
1268 Principal => $self->CurrentUser,
1269 Right => "$right"
1270 )
1271 );
1272
1273}
1274
1275=head2 CurrentUserCanSee
1276
1277Returns true if the current user can see the queue, using SeeQueue
1278
1279=cut
1280
1281sub CurrentUserCanSee {
1282 my $self = shift;
1283
1284 return $self->CurrentUserHasRight('SeeQueue');
1285}
1286
1287
1288=head2 HasRight
1289
1290Takes a param hash with the fields 'Right' and 'Principal'.
1291Principal defaults to the current user.
1292Returns true if the principal has that right for this queue.
1293Returns undef otherwise.
1294
1295=cut
1296
1297# TAKES: Right and optional "Principal" which defaults to the current user
1298sub HasRight {
1299 my $self = shift;
1300 my %args = (
1301 Right => undef,
1302 Principal => $self->CurrentUser,
1303 @_
1304 );
1305 my $principal = delete $args{'Principal'};
1306 unless ( $principal ) {
1307 $RT::Logger->error("Principal undefined in Queue::HasRight");
1308 return undef;
1309 }
1310
1311 return $principal->HasRight(
1312 %args,
1313 Object => ($self->Id ? $self : $RT::System),
1314 );
1315}
1316
1317
1318
1319
1320=head2 id
1321
1322Returns the current value of id.
1323(In the database, id is stored as int(11).)
1324
1325
1326=cut
1327
1328
1329=head2 Name
1330
1331Returns the current value of Name.
1332(In the database, Name is stored as varchar(200).)
1333
1334
1335
1336=head2 SetName VALUE
1337
1338
1339Set Name to VALUE.
1340Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1341(In the database, Name will be stored as a varchar(200).)
1342
1343
1344=cut
1345
1346
1347=head2 Description
1348
1349Returns the current value of Description.
1350(In the database, Description is stored as varchar(255).)
1351
1352
1353
1354=head2 SetDescription VALUE
1355
1356
1357Set Description to VALUE.
1358Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1359(In the database, Description will be stored as a varchar(255).)
1360
1361
1362=cut
1363
1364
1365=head2 CorrespondAddress
1366
1367Returns the current value of CorrespondAddress.
1368(In the database, CorrespondAddress is stored as varchar(120).)
1369
1370
1371
1372=head2 SetCorrespondAddress VALUE
1373
1374
1375Set CorrespondAddress to VALUE.
1376Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1377(In the database, CorrespondAddress will be stored as a varchar(120).)
1378
1379
1380=cut
1381
1382
1383=head2 CommentAddress
1384
1385Returns the current value of CommentAddress.
1386(In the database, CommentAddress is stored as varchar(120).)
1387
1388
1389
1390=head2 SetCommentAddress VALUE
1391
1392
1393Set CommentAddress to VALUE.
1394Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1395(In the database, CommentAddress will be stored as a varchar(120).)
1396
1397
1398=cut
1399
1400
1401=head2 Lifecycle
1402
1403Returns the current value of Lifecycle.
1404(In the database, Lifecycle is stored as varchar(32).)
1405
1406
1407
1408=head2 SetLifecycle VALUE
1409
1410
1411Set Lifecycle to VALUE.
1412Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1413(In the database, Lifecycle will be stored as a varchar(32).)
1414
1415
1416=cut
1417
1418=head2 SubjectTag
1419
1420Returns the current value of SubjectTag.
1421(In the database, SubjectTag is stored as varchar(120).)
1422
1423
1424
1425=head2 SetSubjectTag VALUE
1426
1427
1428Set SubjectTag to VALUE.
1429Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1430(In the database, SubjectTag will be stored as a varchar(120).)
1431
1432
1433=cut
1434
1435
1436=head2 InitialPriority
1437
1438Returns the current value of InitialPriority.
1439(In the database, InitialPriority is stored as int(11).)
1440
1441
1442
1443=head2 SetInitialPriority VALUE
1444
1445
1446Set InitialPriority to VALUE.
1447Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1448(In the database, InitialPriority will be stored as a int(11).)
1449
1450
1451=cut
1452
1453
1454=head2 FinalPriority
1455
1456Returns the current value of FinalPriority.
1457(In the database, FinalPriority is stored as int(11).)
1458
1459
1460
1461=head2 SetFinalPriority VALUE
1462
1463
1464Set FinalPriority to VALUE.
1465Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1466(In the database, FinalPriority will be stored as a int(11).)
1467
1468
1469=cut
1470
1471
1472=head2 DefaultDueIn
1473
1474Returns the current value of DefaultDueIn.
1475(In the database, DefaultDueIn is stored as int(11).)
1476
1477
1478
1479=head2 SetDefaultDueIn VALUE
1480
1481
1482Set DefaultDueIn to VALUE.
1483Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1484(In the database, DefaultDueIn will be stored as a int(11).)
1485
1486
1487=cut
1488
1489
1490=head2 Creator
1491
1492Returns the current value of Creator.
1493(In the database, Creator is stored as int(11).)
1494
1495
1496=cut
1497
1498
1499=head2 Created
1500
1501Returns the current value of Created.
1502(In the database, Created is stored as datetime.)
1503
1504
1505=cut
1506
1507
1508=head2 LastUpdatedBy
1509
1510Returns the current value of LastUpdatedBy.
1511(In the database, LastUpdatedBy is stored as int(11).)
1512
1513
1514=cut
1515
1516
1517=head2 LastUpdated
1518
1519Returns the current value of LastUpdated.
1520(In the database, LastUpdated is stored as datetime.)
1521
1522
1523=cut
1524
1525
1526=head2 Disabled
1527
1528Returns the current value of Disabled.
1529(In the database, Disabled is stored as smallint(6).)
1530
1531
1532
1533=head2 SetDisabled VALUE
1534
1535
1536Set Disabled to VALUE.
1537Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1538(In the database, Disabled will be stored as a smallint(6).)
1539
1540
1541=cut
1542
1543
1544
1545sub _CoreAccessible {
1546 {
1547
1548 id =>
1549 {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
1550 Name =>
1551 {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
1552 Description =>
1553 {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
1554 CorrespondAddress =>
1555 {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
1556 CommentAddress =>
1557 {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
1558 SubjectTag =>
1559 {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
1560 Lifecycle =>
403d7b0b 1561 {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => 'default'},
84fb5b46
MKG
1562 InitialPriority =>
1563 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1564 FinalPriority =>
1565 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1566 DefaultDueIn =>
1567 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1568 Creator =>
1569 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1570 Created =>
1571 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
1572 LastUpdatedBy =>
1573 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1574 LastUpdated =>
1575 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
1576 Disabled =>
1577 {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
1578
1579 }
1580};
1581
1582
1583
1584RT::Base->_ImportOverlays();
1585
15861;