summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Queue.pm
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/Queue.pm')
-rwxr-xr-xrt/lib/RT/Queue.pm1333
1 files changed, 1253 insertions, 80 deletions
diff --git a/rt/lib/RT/Queue.pm b/rt/lib/RT/Queue.pm
index c0a0229cd..3cb87c4be 100755
--- a/rt/lib/RT/Queue.pm
+++ b/rt/lib/RT/Queue.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -46,92 +46,1243 @@
#
# END BPS TAGGED BLOCK }}}
-# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
-#
-# !! DO NOT EDIT THIS FILE !!
-#
-
-use strict;
-
-
=head1 NAME
-RT::Queue
-
+ RT::Queue - an RT Queue object
=head1 SYNOPSIS
+ use RT::Queue;
+
=head1 DESCRIPTION
+An RT queue object.
+
=head1 METHODS
=cut
+
package RT::Queue;
-use RT::Record;
+use strict;
+use warnings;
+use base 'RT::Record';
+
+sub Table {'Queues'}
+
+
+
+use RT::Groups;
+use RT::ACL;
+use RT::Interface::Email;
+
+our @DEFAULT_ACTIVE_STATUS = qw(new open stalled);
+our @DEFAULT_INACTIVE_STATUS = qw(resolved rejected deleted);
+
+# $self->loc('new'); # For the string extractor to get a string to localize
+# $self->loc('open'); # For the string extractor to get a string to localize
+# $self->loc('stalled'); # For the string extractor to get a string to localize
+# $self->loc('resolved'); # For the string extractor to get a string to localize
+# $self->loc('rejected'); # For the string extractor to get a string to localize
+# $self->loc('deleted'); # For the string extractor to get a string to localize
+
+
+our $RIGHTS = {
+ SeeQueue => 'View queue', # loc_pair
+ AdminQueue => 'Create, modify and delete queue', # loc_pair
+ ShowACL => 'Display Access Control List', # loc_pair
+ ModifyACL => 'Create, modify and delete Access Control List entries', # loc_pair
+ ModifyQueueWatchers => 'Modify queue watchers', # loc_pair
+ SeeCustomField => 'View custom field values', # loc_pair
+ ModifyCustomField => 'Modify custom field values', # loc_pair
+ AssignCustomFields => 'Assign and remove queue custom fields', # loc_pair
+ ModifyTemplate => 'Modify Scrip templates', # loc_pair
+ ShowTemplate => 'View Scrip templates', # loc_pair
+
+ ModifyScrips => 'Modify Scrips', # loc_pair
+ ShowScrips => 'View Scrips', # loc_pair
+
+ ShowTicket => 'View ticket summaries', # loc_pair
+ ShowTicketComments => 'View ticket private commentary', # loc_pair
+ ShowOutgoingEmail => 'View exact outgoing email messages and their recipients', # loc_pair
+
+ Watch => 'Sign up as a ticket Requestor or ticket or queue Cc', # loc_pair
+ WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc', # loc_pair
+ CreateTicket => 'Create tickets', # loc_pair
+ ReplyToTicket => 'Reply to tickets', # loc_pair
+ CommentOnTicket => 'Comment on tickets', # loc_pair
+ OwnTicket => 'Own tickets', # loc_pair
+ ModifyTicket => 'Modify tickets', # loc_pair
+ DeleteTicket => 'Delete tickets', # loc_pair
+ TakeTicket => 'Take tickets', # loc_pair
+ StealTicket => 'Steal tickets', # loc_pair
+
+ ForwardMessage => 'Forward messages outside of RT', # loc_pair
+};
+
+our $RIGHT_CATEGORIES = {
+ SeeQueue => 'General',
+ AdminQueue => 'Admin',
+ ShowACL => 'Admin',
+ ModifyACL => 'Admin',
+ ModifyQueueWatchers => 'Admin',
+ SeeCustomField => 'General',
+ ModifyCustomField => 'Staff',
+ AssignCustomFields => 'Admin',
+ ModifyTemplate => 'Admin',
+ ShowTemplate => 'Admin',
+ ModifyScrips => 'Admin',
+ ShowScrips => 'Admin',
+ ShowTicket => 'General',
+ ShowTicketComments => 'Staff',
+ ShowOutgoingEmail => 'Staff',
+ Watch => 'General',
+ WatchAsAdminCc => 'Staff',
+ CreateTicket => 'General',
+ ReplyToTicket => 'General',
+ CommentOnTicket => 'General',
+ OwnTicket => 'Staff',
+ ModifyTicket => 'Staff',
+ DeleteTicket => 'Staff',
+ TakeTicket => 'Staff',
+ StealTicket => 'Staff',
+ ForwardMessage => 'Staff',
+};
+
+# Tell RT::ACE that this sort of object can get acls granted
+$RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
+
+# TODO: This should be refactored out into an RT::ACLedObject or something
+# stuff the rights into a hash of rights that can exist.
+
+__PACKAGE__->AddRights(%$RIGHTS);
+__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
+require RT::Lifecycle;
+
+=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
+
+Adds the given rights to the list of possible rights. This method
+should be called during server startup, not at runtime.
+
+=cut
+
+sub AddRights {
+ my $self = shift;
+ my %new = @_;
+ $RIGHTS = { %$RIGHTS, %new };
+ %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
+ map { lc($_) => $_ } keys %new);
+}
+
+=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
+
+Adds the given right and category pairs to the list of right categories. This
+method should be called during server startup, not at runtime.
+
+=cut
+
+sub AddRightCategories {
+ my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
+ my %new = @_;
+ $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
+}
+
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ return $self->SUPER::_AddLink(%args);
+}
+
+sub DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ #check acls
+ unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
+ $RT::Logger->debug("No permission to delete links");
+ return ( 0, $self->loc('Permission Denied'))
+ }
+
+ return $self->SUPER::_DeleteLink(%args);
+}
+
+=head2 AvailableRights
+
+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
+
+=cut
+
+sub AvailableRights {
+ my $self = shift;
+ return($RIGHTS);
+}
+
+=head2 RightCategories
+
+Returns a hashref where the keys are rights for this type of object and the
+values are the category (General, Staff, Admin) the right falls into.
+
+=cut
+
+sub RightCategories {
+ return $RIGHT_CATEGORIES;
+}
+
+
+sub Lifecycle {
+ my $self = shift;
+ unless (ref $self && $self->id) {
+ return RT::Lifecycle->Load('')
+ }
+
+ my $name = $self->_Value( Lifecycle => @_ );
+ $name ||= 'default';
+
+ my $res = RT::Lifecycle->Load( $name );
+ unless ( $res ) {
+ $RT::Logger->error("Lifecycle '$name' for queue '".$self->Name."' doesn't exist");
+ return RT::Lifecycle->Load('default');
+ }
+ return $res;
+}
+
+sub SetLifecycle {
+ my $self = shift;
+ my $value = shift;
+
+ if ( $value && $value ne 'default' ) {
+ return (0, $self->loc('[_1] is not valid lifecycle', $value ))
+ unless $self->ValidateLifecycle( $value );
+ } else {
+ $value = undef;
+ }
+
+ return $self->_Set( Field => 'Lifecycle', Value => $value, @_ );
+}
+
+=head2 ValidateLifecycle NAME
+
+Takes a lifecycle name. Returns true if it's an ok name and such
+lifecycle is configured. Returns undef otherwise.
+
+=cut
+
+sub ValidateLifecycle {
+ my $self = shift;
+ my $value = shift;
+ return undef unless RT::Lifecycle->Load( $value );
+ return 1;
+}
+
+
+=head2 ActiveStatusArray
+
+Returns an array of all ActiveStatuses for this queue
+
+=cut
+
+sub ActiveStatusArray {
+ my $self = shift;
+ return $self->Lifecycle->Valid('initial', 'active');
+}
+
+=head2 InactiveStatusArray
+
+Returns an array of all InactiveStatuses for this queue
+
+=cut
+
+sub InactiveStatusArray {
+ my $self = shift;
+ return $self->Lifecycle->Inactive;
+}
+
+=head2 StatusArray
+
+Returns an array of all statuses for this queue
+
+=cut
+
+sub StatusArray {
+ my $self = shift;
+ return $self->Lifecycle->Valid( @_ );
+}
+
+=head2 IsValidStatus value
-use vars qw( @ISA );
-@ISA= qw( RT::Record );
+Returns true if value is a valid status. Otherwise, returns 0.
-sub _Init {
- my $self = shift;
+=cut
- $self->Table('Queues');
- $self->SUPER::_Init(@_);
+sub IsValidStatus {
+ my $self = shift;
+ return $self->Lifecycle->IsValid( shift );
}
+=head2 IsActiveStatus value
+
+Returns true if value is a Active status. Otherwise, returns 0
+=cut
+
+sub IsActiveStatus {
+ my $self = shift;
+ return $self->Lifecycle->IsValid( shift, 'initial', 'active');
+}
-=head2 Create PARAMHASH
+=head2 IsInactiveStatus value
-Create takes a hash of values and creates a row in the database:
+Returns true if value is a Inactive status. Otherwise, returns 0
- varchar(200) 'Name'.
- varchar(255) 'Description'.
- varchar(120) 'CorrespondAddress'.
- varchar(120) 'CommentAddress'.
- int(11) 'InitialPriority'.
- int(11) 'FinalPriority'.
- int(11) 'DefaultDueIn'.
- smallint(6) 'Disabled'.
=cut
+sub IsInactiveStatus {
+ my $self = shift;
+ return $self->Lifecycle->IsInactive( shift );
+}
+
+
+
+
+
+
+=head2 Create(ARGS)
+
+Arguments: ARGS is a hash of named parameters. Valid parameters are:
+ Name (required)
+ Description
+ CorrespondAddress
+ CommentAddress
+ InitialPriority
+ FinalPriority
+ DefaultDueIn
+
+If you pass the ACL check, it creates the queue and returns its queue id.
+=cut
+
sub Create {
my $self = shift;
- my %args = (
- Name => '',
- Description => '',
- CorrespondAddress => '',
- CommentAddress => '',
- InitialPriority => '0',
- FinalPriority => '0',
- DefaultDueIn => '0',
- Disabled => '0',
+ my %args = (
+ Name => undef,
+ Description => '',
+ CorrespondAddress => '',
+ CommentAddress => '',
+ Lifecycle => 'default',
+ SubjectTag => undef,
+ InitialPriority => 0,
+ FinalPriority => 0,
+ DefaultDueIn => 0,
+ Sign => undef,
+ Encrypt => undef,
+ _RecordTransaction => 1,
+ @_
+ );
+
+ unless ( $self->CurrentUser->HasRight(Right => 'AdminQueue', Object => $RT::System) )
+ { #Check them ACLs
+ return ( 0, $self->loc("No permission to create queues") );
+ }
+
+ {
+ my ($val, $msg) = $self->_ValidateName( $args{'Name'} );
+ return ($val, $msg) unless $val;
+ }
+
+ if ( $args{'Lifecycle'} && $args{'Lifecycle'} ne 'default' ) {
+ return ( 0, $self->loc('Invalid lifecycle name') )
+ unless $self->ValidateLifecycle( $args{'Lifecycle'} );
+ } else {
+ $args{'Lifecycle'} = undef;
+ }
+
+ my %attrs = map {$_ => 1} $self->ReadableAttributes;
+
+ #TODO better input validation
+ $RT::Handle->BeginTransaction();
+ my $id = $self->SUPER::Create( map { $_ => $args{$_} } grep exists $args{$_}, keys %attrs );
+ unless ($id) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Queue could not be created') );
+ }
+
+ my $create_ret = $self->_CreateQueueGroups();
+ unless ($create_ret) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Queue could not be created') );
+ }
+ if ( $args{'_RecordTransaction'} ) {
+ $self->_NewTransaction( Type => "Create" );
+ }
+ $RT::Handle->Commit;
+
+ if ( defined $args{'Sign'} ) {
+ my ($status, $msg) = $self->SetSign( $args{'Sign'} );
+ $RT::Logger->error("Couldn't set attribute 'Sign': $msg")
+ unless $status;
+ }
+ if ( defined $args{'Encrypt'} ) {
+ my ($status, $msg) = $self->SetEncrypt( $args{'Encrypt'} );
+ $RT::Logger->error("Couldn't set attribute 'Encrypt': $msg")
+ unless $status;
+ }
+
+ RT->System->QueueCacheNeedsUpdate(1);
+
+ return ( $id, $self->loc("Queue created") );
+}
+
+
+
+sub Delete {
+ my $self = shift;
+ return ( 0,
+ $self->loc('Deleting this object would break referential integrity') );
+}
+
+
+
+=head2 SetDisabled
+
+Takes a boolean.
+1 will cause this queue to no longer be available for tickets.
+0 will re-enable this queue.
+
+=cut
+
+sub SetDisabled {
+ my $self = shift;
+ my $val = shift;
+
+ $RT::Handle->BeginTransaction();
+ my $set_err = $self->_Set( Field =>'Disabled', Value => $val);
+ unless ($set_err) {
+ $RT::Handle->Rollback();
+ $RT::Logger->warning("Couldn't ".($val == 1) ? "disable" : "enable"." queue ".$self->PrincipalObj->Id);
+ return (undef);
+ }
+ $self->_NewTransaction( Type => ($val == 1) ? "Disabled" : "Enabled" );
+
+ $RT::Handle->Commit();
+
+ RT->System->QueueCacheNeedsUpdate(1);
+
+ if ( $val == 1 ) {
+ return (1, $self->loc("Queue disabled"));
+ } else {
+ return (1, $self->loc("Queue enabled"));
+ }
+
+}
+
+
+
+=head2 Load
+
+Takes either a numerical id or a textual Name and loads the specified queue.
+
+=cut
+
+sub Load {
+ my $self = shift;
+
+ my $identifier = shift;
+ if ( !$identifier ) {
+ return (undef);
+ }
+
+ if ( $identifier =~ /^(\d+)$/ ) {
+ $self->SUPER::LoadById($identifier);
+ }
+ else {
+ $self->LoadByCols( Name => $identifier );
+ }
+
+ return ( $self->Id );
+
+}
+
+
+
+=head2 ValidateName NAME
+
+Takes a queue name. Returns true if it's an ok name for
+a new queue. Returns undef if there's already a queue by that name.
+
+=cut
+
+sub ValidateName {
+ my $self = shift;
+ my $name = shift;
+
+ my ($ok, $msg) = $self->_ValidateName($name);
+
+ return $ok ? 1 : 0;
+}
+
+sub _ValidateName {
+ my $self = shift;
+ my $name = shift;
+
+ return (undef, "Queue name is required") unless length $name;
+
+ # Validate via the superclass first
+ # Case: short circuit if it's an integer so we don't have
+ # fale negatives when loading a temp queue
+ unless ( my $q = $self->SUPER::ValidateName($name) ) {
+ return ($q, $self->loc("'[_1]' is not a valid name.", $name));
+ }
+
+ my $tempqueue = RT::Queue->new(RT->SystemUser);
+ $tempqueue->Load($name);
+
+ #If this queue exists, return undef
+ if ( $tempqueue->Name() && $tempqueue->id != $self->id) {
+ return (undef, $self->loc("Queue already exists") );
+ }
+
+ return (1);
+}
+
+
+=head2 SetSign
+
+=cut
+
+sub Sign {
+ my $self = shift;
+ my $value = shift;
+
+ return undef unless $self->CurrentUserHasRight('SeeQueue');
+ my $attr = $self->FirstAttribute('Sign') or return 0;
+ return $attr->Content;
+}
+
+sub SetSign {
+ my $self = shift;
+ my $value = shift;
+
+ return ( 0, $self->loc('Permission Denied') )
+ unless $self->CurrentUserHasRight('AdminQueue');
+
+ my ($status, $msg) = $self->SetAttribute(
+ Name => 'Sign',
+ Description => 'Sign outgoing messages by default',
+ Content => $value,
+ );
+ return ($status, $msg) unless $status;
+ return ($status, $self->loc('Signing enabled')) if $value;
+ return ($status, $self->loc('Signing disabled'));
+}
+
+sub Encrypt {
+ my $self = shift;
+ my $value = shift;
+
+ return undef unless $self->CurrentUserHasRight('SeeQueue');
+ my $attr = $self->FirstAttribute('Encrypt') or return 0;
+ return $attr->Content;
+}
+
+sub SetEncrypt {
+ my $self = shift;
+ my $value = shift;
+
+ return ( 0, $self->loc('Permission Denied') )
+ unless $self->CurrentUserHasRight('AdminQueue');
+
+ my ($status, $msg) = $self->SetAttribute(
+ Name => 'Encrypt',
+ Description => 'Encrypt outgoing messages by default',
+ Content => $value,
+ );
+ return ($status, $msg) unless $status;
+ return ($status, $self->loc('Encrypting enabled')) if $value;
+ return ($status, $self->loc('Encrypting disabled'));
+}
+
+=head2 Templates
+
+Returns an RT::Templates object of all of this queue's templates.
+
+=cut
+
+sub Templates {
+ my $self = shift;
+
+ my $templates = RT::Templates->new( $self->CurrentUser );
+
+ if ( $self->CurrentUserHasRight('ShowTemplate') ) {
+ $templates->LimitToQueue( $self->id );
+ }
+
+ return ($templates);
+}
+
+
+
+
+=head2 CustomField NAME
+
+Load the queue-specific custom field named NAME
+
+=cut
+
+sub CustomField {
+ my $self = shift;
+ my $name = shift;
+ my $cf = RT::CustomField->new($self->CurrentUser);
+ $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id);
+ return ($cf);
+}
+
+
+
+=head2 TicketCustomFields
+
+Returns an L<RT::CustomFields> object containing all global and
+queue-specific B<ticket> custom fields.
+
+=cut
+
+sub TicketCustomFields {
+ my $self = shift;
+
+ my $cfs = RT::CustomFields->new( $self->CurrentUser );
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $cfs->SetContextObject( $self );
+ $cfs->LimitToGlobalOrObjectId( $self->Id );
+ $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+ $cfs->ApplySortOrder;
+ }
+ return ($cfs);
+}
+
+
+
+=head2 TicketTransactionCustomFields
+
+Returns an L<RT::CustomFields> object containing all global and
+queue-specific B<transaction> custom fields.
+
+=cut
+
+sub TicketTransactionCustomFields {
+ my $self = shift;
+
+ my $cfs = RT::CustomFields->new( $self->CurrentUser );
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $cfs->LimitToGlobalOrObjectId( $self->Id );
+ $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' );
+ $cfs->ApplySortOrder;
+ }
+ return ($cfs);
+}
+
+
+
+
+
+=head2 AllRoleGroupTypes
+
+Returns a list of the names of the various role group types that this queue
+has, including Requestor and Owner. If you don't want them, see
+L</ManageableRoleGroupTypes>.
+
+=cut
+
+sub AllRoleGroupTypes {
+ my $self = shift;
+ return ($self->ManageableRoleGroupTypes, qw(Requestor Owner));
+}
+
+=head2 IsRoleGroupType
+
+Returns whether the passed-in type is a role group type.
+
+=cut
+
+sub IsRoleGroupType {
+ my $self = shift;
+ my $type = shift;
+
+ for my $valid_type ($self->AllRoleGroupTypes) {
+ return 1 if $type eq $valid_type;
+ }
+
+ return 0;
+}
+
+=head2 ManageableRoleGroupTypes
+
+Returns a list of the names of the various role group types that this queue
+has, excluding Requestor and Owner. If you want them, see L</AllRoleGroupTypes>.
+
+=cut
+
+sub ManageableRoleGroupTypes {
+ return qw(Cc AdminCc);
+}
+
+=head2 IsManageableRoleGroupType
+
+Returns whether the passed-in type is a manageable role group type.
+
+=cut
+
+sub IsManageableRoleGroupType {
+ my $self = shift;
+ my $type = shift;
+
+ for my $valid_type ($self->ManageableRoleGroupTypes) {
+ return 1 if $type eq $valid_type;
+ }
+
+ return 0;
+}
+
+
+=head2 _CreateQueueGroups
+
+Create the ticket groups and links for this ticket.
+This routine expects to be called from Ticket->Create _inside of a transaction_
+
+It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
+
+It will return true on success and undef on failure.
+
+
+=cut
+
+sub _CreateQueueGroups {
+ my $self = shift;
+
+ my @types = $self->AllRoleGroupTypes;
+
+ foreach my $type (@types) {
+ my $ok = $self->_CreateQueueRoleGroup($type);
+ return undef if !$ok;
+ }
+
+ return 1;
+}
+
+sub _CreateQueueRoleGroup {
+ my $self = shift;
+ my $type = shift;
+
+ my $type_obj = RT::Group->new($self->CurrentUser);
+ my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id,
+ Type => $type,
+ Domain => 'RT::Queue-Role');
+ unless ($id) {
+ $RT::Logger->error("Couldn't create a Queue group of type '$type' for queue ".
+ $self->Id.": ".$msg);
+ return(undef);
+ }
+
+ return $id;
+}
+
+
+
+# _HasModifyWatcherRight {{{
+sub _HasModifyWatcherRight {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+ return 1 if $self->CurrentUserHasRight('ModifyQueueWatchers');
+
+ #If the watcher we're trying to add is for the current user
+ if ( defined $args{'PrincipalId'} && $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
+ if ( $args{'Type'} eq 'AdminCc' ) {
+ return 1 if $self->CurrentUserHasRight('WatchAsAdminCc');
+ }
+ elsif ( $args{'Type'} eq 'Cc' or $args{'Type'} eq 'Requestor' ) {
+ return 1 if $self->CurrentUserHasRight('Watch');
+ }
+ else {
+ $RT::Logger->warning( "$self -> _HasModifyWatcher got passed a bogus type $args{Type}");
+ return ( 0, $self->loc('Invalid queue role group type [_1]', $args{Type}) );
+ }
+ }
+
+ return ( 0, $self->loc("Permission Denied") );
+}
+
+
+=head2 AddWatcher
+
+AddWatcher takes a parameter hash. The keys are as follows:
+
+Type One of Requestor, Cc, AdminCc
+
+PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
+Email The email address of the new watcher. If a user with this
+ email address can't be found, a new nonprivileged user will be created.
+
+If 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.
+
+Returns a tuple of (status/id, message).
+
+=cut
+
+sub AddWatcher {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+ return ( 0, "No principal specified" )
+ unless $args{'Email'} or $args{'PrincipalId'};
+
+ if ( !$args{'PrincipalId'} && $args{'Email'} ) {
+ my $user = RT::User->new( $self->CurrentUser );
+ $user->LoadByEmail( $args{'Email'} );
+ $args{'PrincipalId'} = $user->PrincipalId if $user->id;
+ }
+
+ return ( 0, "Unknown watcher type [_1]", $args{Type} )
+ unless $self->IsRoleGroupType($args{Type});
+
+ my ($ok, $msg) = $self->_HasModifyWatcherRight(%args);
+ return ($ok, $msg) if !$ok;
+
+ return $self->_AddWatcher(%args);
+}
+
+#This contains the meat of AddWatcher. but can be called from a routine like
+# Create, which doesn't need the additional acl check
+sub _AddWatcher {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ Silent => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ if ( $args{'PrincipalId'} ) {
+ $principal->Load( $args{'PrincipalId'} );
+ if ( $principal->id and $principal->IsUser and my $email = $principal->Object->EmailAddress ) {
+ 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'})))
+ if RT::EmailParser->IsRTAddress( $email );
+ }
+ }
+ elsif ( $args{'Email'} ) {
+ if ( RT::EmailParser->IsRTAddress( $args{'Email'} ) ) {
+ 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'})));
+ }
+ my $user = RT::User->new($self->CurrentUser);
+ $user->LoadByEmail( $args{'Email'} );
+ $user->Load( $args{'Email'} )
+ unless $user->id;
+
+ if ( $user->Id ) { # If the user exists
+ $principal->Load( $user->PrincipalId );
+ } else {
+ # if the user doesn't exist, we need to create a new user
+ my $new_user = RT::User->new(RT->SystemUser);
+
+ my ( $Address, $Name ) =
+ RT::Interface::Email::ParseAddressFromHeader($args{'Email'});
+
+ my ( $Val, $Message ) = $new_user->Create(
+ Name => $Address,
+ EmailAddress => $Address,
+ RealName => $Name,
+ Privileged => 0,
+ Comments => 'Autocreated when added as a watcher'
+ );
+ unless ($Val) {
+ $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message);
+ # Deal with the race condition of two account creations at once
+ $new_user->LoadByEmail( $args{'Email'} );
+ }
+ $principal->Load( $new_user->PrincipalId );
+ }
+ }
+ # If we can't find this watcher, we need to bail.
+ unless ( $principal->Id ) {
+ return(0, $self->loc("Could not find or create that user"));
+ }
+
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
+ unless ($group->id) {
+ return(0,$self->loc("Group not found"));
+ }
+
+ if ( $group->HasMember( $principal)) {
+
+ return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
+ }
+
+
+ my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
+ unless ($m_id) {
+ $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg);
+
+ return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
+ }
+ return ( 1, $self->loc("Added [_1] to members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
+}
+
+
+
+=head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
+
+
+Deletes a queue watcher. Takes two arguments:
+
+Type (one of Requestor,Cc,AdminCc)
+
+and one of
+
+PrincipalId (an RT::Principal Id of the watcher you want to remove)
+ OR
+Email (the email address of an existing wathcer)
+
+
+=cut
+
+
+sub DeleteWatcher {
+ my $self = shift;
+
+ my %args = ( Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_ );
+
+ unless ( $args{'PrincipalId'} || $args{'Email'} ) {
+ return ( 0, $self->loc("No principal specified") );
+ }
+
+ if ( !$args{PrincipalId} and $args{Email} ) {
+ my $user = RT::User->new( $self->CurrentUser );
+ my ($rv, $msg) = $user->LoadByEmail( $args{Email} );
+ $args{PrincipalId} = $user->PrincipalId if $rv;
+ }
+
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ if ( $args{'PrincipalId'} ) {
+ $principal->Load( $args{'PrincipalId'} );
+ }
+ else {
+ my $user = RT::User->new( $self->CurrentUser );
+ $user->LoadByEmail( $args{'Email'} );
+ $principal->Load( $user->Id );
+ }
+
+ # If we can't find this watcher, we need to bail.
+ unless ( $principal->Id ) {
+ return ( 0, $self->loc("Could not find that principal") );
+ }
+
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
+ unless ($group->id) {
+ return(0,$self->loc("Group not found"));
+ }
+
+ return ( 0, $self->loc('Unknown watcher type [_1]', $args{Type}) )
+ unless $self->IsRoleGroupType($args{Type});
+
+ my ($ok, $msg) = $self->_HasModifyWatcherRight(%args);
+ return ($ok, $msg) if !$ok;
+
+ # see if this user is already a watcher.
+
+ unless ( $group->HasMember($principal)) {
+ return ( 0,
+ $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
+ }
+
+ my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
+ unless ($m_id) {
+ $RT::Logger->error("Failed to delete ".$principal->Id.
+ " as a member of group ".$group->Id.": ".$m_msg);
+
+ return ( 0, $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
+ }
+
+ return ( 1, $self->loc("Removed [_1] from members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
+}
+
+
+
+=head2 AdminCcAddresses
+
+returns String: All queue AdminCc email addresses as a string
+
+=cut
+
+sub AdminCcAddresses {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return undef;
+ }
+
+ return ( $self->AdminCc->MemberEmailAddressesAsString )
+
+}
+
+
+
+=head2 CcAddresses
+
+returns String: All queue Ccs as a string of email addresses
+
+=cut
+
+sub CcAddresses {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return undef;
+ }
- @_);
- $self->SUPER::Create(
- Name => $args{'Name'},
- Description => $args{'Description'},
- CorrespondAddress => $args{'CorrespondAddress'},
- CommentAddress => $args{'CommentAddress'},
- InitialPriority => $args{'InitialPriority'},
- FinalPriority => $args{'FinalPriority'},
- DefaultDueIn => $args{'DefaultDueIn'},
- Disabled => $args{'Disabled'},
-);
+ return ( $self->Cc->MemberEmailAddressesAsString);
}
+=head2 Cc
+
+Takes nothing.
+Returns an RT::Group object which contains this Queue's Ccs.
+If the user doesn't have "ShowQueue" permission, returns an empty group
+
+=cut
+
+sub Cc {
+ my $self = shift;
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
+ }
+ return ($group);
+
+}
+
+
+
+=head2 AdminCc
+
+Takes nothing.
+Returns an RT::Group object which contains this Queue's AdminCcs.
+If the user doesn't have "ShowQueue" permission, returns an empty group
+
+=cut
+
+sub AdminCc {
+ my $self = shift;
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
+ }
+ return ($group);
+
+}
+
+
+
+# a generic routine to be called by IsRequestor, IsCc and IsAdminCc
+
+=head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
+
+Takes a param hash with the attributes Type and PrincipalId
+
+Type is one of Requestor, Cc, AdminCc and Owner
+
+PrincipalId is an RT::Principal id
+
+Returns true if that principal is a member of the group Type for this queue
+
+
+=cut
+
+sub IsWatcher {
+ my $self = shift;
+
+ my %args = ( Type => 'Cc',
+ PrincipalId => undef,
+ @_
+ );
+
+ # Load the relevant group.
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->id);
+ # Ask if it has the member in question
+
+ my $principal = RT::Principal->new($self->CurrentUser);
+ $principal->Load($args{'PrincipalId'});
+ unless ($principal->Id) {
+ return (undef);
+ }
+
+ return ($group->HasMemberRecursively($principal));
+}
+
+
+
+
+=head2 IsCc PRINCIPAL_ID
+
+Takes an RT::Principal id.
+Returns true if the principal is a requestor of the current queue.
+
+
+=cut
+
+sub IsCc {
+ my $self = shift;
+ my $cc = shift;
+
+ return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
+
+}
+
+
+
+=head2 IsAdminCc PRINCIPAL_ID
+
+Takes an RT::Principal id.
+Returns true if the principal is a requestor of the current queue.
+
+=cut
+
+sub IsAdminCc {
+ my $self = shift;
+ my $person = shift;
+
+ return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
+
+}
+
+
+
+
+
+
+
+
+
+
+sub _Set {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminQueue') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ return ( $self->SUPER::_Set(@_) );
+}
+
+
+
+sub _Value {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return (undef);
+ }
+
+ return ( $self->__Value(@_) );
+}
+
+
+
+=head2 CurrentUserHasRight
+
+Takes one argument. A textual string with the name of the right we want to check.
+Returns true if the current user has that right for this queue.
+Returns undef otherwise.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ return (
+ $self->HasRight(
+ Principal => $self->CurrentUser,
+ Right => "$right"
+ )
+ );
+
+}
+
+
+
+=head2 HasRight
+
+Takes a param hash with the fields 'Right' and 'Principal'.
+Principal defaults to the current user.
+Returns true if the principal has that right for this queue.
+Returns undef otherwise.
+
+=cut
+
+# TAKES: Right and optional "Principal" which defaults to the current user
+sub HasRight {
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Principal => $self->CurrentUser,
+ @_
+ );
+ my $principal = delete $args{'Principal'};
+ unless ( $principal ) {
+ $RT::Logger->error("Principal undefined in Queue::HasRight");
+ return undef;
+ }
+
+ return $principal->HasRight(
+ %args,
+ Object => ($self->Id ? $self : $RT::System),
+ );
+}
+
+
+
+
=head2 id
Returns the current value of id.
@@ -213,6 +1364,41 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
+=head2 Lifecycle
+
+Returns the current value of Lifecycle.
+(In the database, Lifecycle is stored as varchar(32).)
+
+
+
+=head2 SetLifecycle VALUE
+
+
+Set Lifecycle to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Lifecycle will be stored as a varchar(32).)
+
+
+=cut
+
+=head2 SubjectTag
+
+Returns the current value of SubjectTag.
+(In the database, SubjectTag is stored as varchar(120).)
+
+
+
+=head2 SetSubjectTag VALUE
+
+
+Set SubjectTag to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, SubjectTag will be stored as a varchar(120).)
+
+
+=cut
+
+
=head2 InitialPriority
Returns the current value of InitialPriority.
@@ -326,54 +1512,41 @@ sub _CoreAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
CorrespondAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
CommentAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ SubjectTag =>
+ {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ Lifecycle =>
+ {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => ''},
InitialPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
FinalPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
DefaultDueIn =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
Disabled =>
- {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
}
};
-RT::Base->_ImportOverlays();
-
-=head1 SEE ALSO
-
-This class allows "overlay" methods to be placed
-into the following files _Overlay is for a System overlay by the original author,
-_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
-These overlay files can contain new subs or subs to replace existing subs in this module.
-
-Each of these files should begin with the line
-
- no warnings qw(redefine);
-
-so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
-
-RT::Queue_Overlay, RT::Queue_Vendor, RT::Queue_Local
-
-=cut
+RT::Base->_ImportOverlays();
1;