-# $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Queue.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
+# <sales@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
=head1 NAME
=head1 DESCRIPTION
+An RT queue object.
=head1 METHODS
-=begin testing
-use RT::TestHarness;
+=cut
+
-use RT::Queue;
+package RT::Queue;
-=end testing
+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> [, ...]
-package RT::Queue;
-use RT::Record;
+Adds the given right and category pairs to the list of right categories. This
+method should be called during server startup, not at runtime.
-@ISA= qw(RT::Record);
+=cut
-use vars (@STATUS);
+sub AddRightCategories {
+ my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
+ my %new = @_;
+ $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
+}
-@STATUS = qw(new open stalled resolved dead);
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
-=head2 StatusArray
+ return $self->SUPER::_AddLink(%args);
+}
-Returns an array of all statuses for this queue
+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 StatusArray {
+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;
- return (@STATUS);
+ 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 || 'default';
+
+ return ( 0, $self->loc( '[_1] is not a valid lifecycle', $value ) )
+ unless $self->ValidateLifecycle($value);
-=head2 IsValidStatus VALUE
+ return $self->_Set( Field => 'Lifecycle', Value => $value, @_ );
+}
-Returns true if VALUE is a valid status. Otherwise, returns 0
+=head2 ValidateLifecycle NAME
-=for testing
-my $q = new RT::Queue($RT::SystemUser);
-ok($q->IsValidStatus('new')== 1, 'New is a valid status');
-ok($q->IsValidStatus('f00')== 0, 'f00 is not a valid status');
+Takes a lifecycle name. Returns true if it's an ok name and such
+lifecycle is configured. Returns undef otherwise.
=cut
-sub IsValidStatus {
- my $self = shift;
- my $value = shift;
+sub ValidateLifecycle {
+ my $self = shift;
+ my $value = shift;
+ return undef unless RT::Lifecycle->Load( $value );
+ return 1;
+}
+
+
+=head2 ActiveStatusArray
- my $retval = grep (/^$value$/, $self->StatusArray);
- return ($retval);
+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
-# {{{ sub _Init
-sub _Init {
+=cut
+
+sub InactiveStatusArray {
my $self = shift;
- $self->{'table'} = "Queues";
- return ($self->SUPER::_Init(@_));
+ return $self->Lifecycle->Inactive;
}
-# }}}
-# {{{ sub _Accessible
+=head2 StatusArray
+
+Returns an array of all statuses for this queue
+
+=cut
-sub _Accessible {
+sub StatusArray {
my $self = shift;
- my %Cols = ( Name => 'read/write',
- CorrespondAddress => 'read/write',
- Description => 'read/write',
- CommentAddress => 'read/write',
- InitialPriority => 'read/write',
- FinalPriority => 'read/write',
- DefaultDueIn => 'read/write',
- Creator => 'read/auto',
- Created => 'read/auto',
- LastUpdatedBy => 'read/auto',
- LastUpdated => 'read/auto',
- Disabled => 'read/write',
-
- );
- return($self->SUPER::_Accessible(@_, %Cols));
+ return $self->Lifecycle->Valid( @_ );
}
-# }}}
+=head2 IsValidStatus value
-# {{{ sub Create
+Returns true if value is a valid status. Otherwise, returns 0.
-=head2 Create
+=cut
+
+sub IsValidStatus {
+ my $self = shift;
+ return $self->Lifecycle->IsValid( shift );
+}
-Create takes the name of the new queue
+=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 IsInactiveStatus value
+
+Returns true if value is a Inactive status. Otherwise, returns 0
+
+
+=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 {
+sub Create {
my $self = shift;
- my %args = ( Name => undef,
- CorrespondAddress => '',
- Description => '',
- CommentAddress => '',
- InitialPriority => "0",
- FinalPriority => "0",
- DefaultDueIn => "0",
- @_);
-
- unless ($self->CurrentUser->HasSystemRight('AdminQueue')) { #Check them ACLs
- return (0, "No permission to create queues")
+ my %args = (
+ Name => undef,
+ Description => '',
+ CorrespondAddress => '',
+ CommentAddress => '',
+ Lifecycle => 'default',
+ SubjectTag => undef,
+ InitialPriority => 0,
+ FinalPriority => 0,
+ DefaultDueIn => 0,
+ Sign => undef,
+ SignAuto => 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") );
}
- unless ($self->ValidateName($args{'Name'})) {
- return(0, 'Queue already exists');
+ {
+ my ($val, $msg) = $self->_ValidateName( $args{'Name'} );
+ return ($val, $msg) unless $val;
}
+
+ $args{'Lifecycle'} ||= 'default';
+
+ return ( 0, $self->loc('[_1] is not a valid lifecycle', $args{'Lifecycle'} ) )
+ unless $self->ValidateLifecycle( $args{'Lifecycle'} );
+
+ my %attrs = map {$_ => 1} $self->ReadableAttributes;
+
#TODO better input validation
-
- my $id = $self->SUPER::Create(%args);
+ $RT::Handle->BeginTransaction();
+ my $id = $self->SUPER::Create( map { $_ => $args{$_} } grep exists $args{$_}, keys %attrs );
unless ($id) {
- return (0, 'Queue could not be created');
+ $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;
+
+ for my $attr (qw/Sign SignAuto Encrypt/) {
+ next unless defined $args{$attr};
+ my $set = "Set" . $attr;
+ my ($status, $msg) = $self->$set( $args{$attr} );
+ $RT::Logger->error("Couldn't set attribute '$attr': $msg")
+ unless $status;
}
- return ($id, "Queue $id created");
+ RT->System->QueueCacheNeedsUpdate(1);
+
+ return ( $id, $self->loc("Queue created") );
}
-# }}}
-# {{{ sub Delete
sub Delete {
my $self = shift;
- return (0, 'Deleting this object would break referential integrity');
+ return ( 0,
+ $self->loc('Deleting this object would break referential integrity') );
}
-# }}}
-# {{{ sub SetDisabled
=head2 SetDisabled
Takes a boolean.
-1 will cause this queue to no longer be avaialble for tickets.
-0 will re-enable this queue
+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"));
+ }
+
+}
+
-# {{{ sub Load
=head2 Load
Takes either a numerical id or a textual Name and loads the specified queue.
-
+
=cut
-sub Load {
+sub Load {
my $self = shift;
-
+
my $identifier = shift;
- if (!$identifier) {
- return (undef);
- }
-
- if ($identifier !~ /\D/) {
- $self->SUPER::LoadById($identifier);
+ if ( !$identifier ) {
+ return (undef);
+ }
+
+ if ( $identifier =~ /^(\d+)$/ ) {
+ $self->SUPER::LoadById($identifier);
}
else {
- $self->LoadByCol("Name", $identifier);
+ $self->LoadByCols( Name => $identifier );
}
- return ($self->Id);
-
+ return ( $self->Id );
}
-# }}}
-# {{{ sub ValidateName
+
=head2 ValidateName NAME
sub ValidateName {
my $self = shift;
my $name = shift;
-
- my $tempqueue = new RT::Queue($RT::SystemUser);
- $tempqueue->Load($name);
- #If we couldn't load it :)
- unless ($tempqueue->id()) {
- return(1);
- }
+ my ($ok, $msg) = $self->_ValidateName($name);
- #If this queue exists, return undef
- #Avoid the ACL check.
- if ($tempqueue->Name()){
- return(undef);
+ 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));
}
- #If the queue doesn't exist, return 1
- else {
- return(1);
+ 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
-# {{{ sub Templates
+=cut
-=head2 Templates
+sub Sign {
+ my $self = shift;
+ my $value = shift;
-Returns an RT::Templates object of all of this queue's templates.
+ return undef unless $self->CurrentUserHasRight('SeeQueue');
+ my $attr = $self->FirstAttribute('Sign') or return 0;
+ return $attr->Content;
+}
-=cut
+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 Templates {
+sub SignAuto {
my $self = shift;
-
+ my $value = shift;
- my $templates = RT::Templates->new($self->CurrentUser);
+ return undef unless $self->CurrentUserHasRight('SeeQueue');
+ my $attr = $self->FirstAttribute('SignAuto') or return 0;
+ return $attr->Content;
+}
- if ($self->CurrentUserHasRight('ShowTemplate')) {
- $templates->LimitToQueue($self->id);
- }
-
- return ($templates);
+sub SetSignAuto {
+ my $self = shift;
+ my $value = shift;
+
+ return ( 0, $self->loc('Permission Denied') )
+ unless $self->CurrentUserHasRight('AdminQueue');
+
+ my ($status, $msg) = $self->SetAttribute(
+ Name => 'SignAuto',
+ Description => 'Sign auto-generated outgoing messages',
+ 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;
-# {{{ Dealing with watchers
+ return undef unless $self->CurrentUserHasRight('SeeQueue');
+ my $attr = $self->FirstAttribute('Encrypt') or return 0;
+ return $attr->Content;
+}
-# {{{ sub Watchers
+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 Watchers
+=head2 Templates
-Watchers returns a Watchers object preloaded with this queue\'s watchers.
+Returns an RT::Templates object of all of this queue's templates.
=cut
-sub Watchers {
+sub Templates {
my $self = shift;
-
- require RT::Watchers;
- my $watchers =RT::Watchers->new($self->CurrentUser);
-
- if ($self->CurrentUserHasRight('SeeQueue')) {
- $watchers->LimitToQueue($self->id);
- }
-
- return($watchers);
-}
-
-# }}}
-
-# {{{ sub WatchersAsString
-=head2 WatchersAsString
-Returns a string of all queue watchers email addresses concatenated with ','s.
+ my $templates = RT::Templates->new( $self->CurrentUser );
-=cut
+ if ( $self->CurrentUserHasRight('ShowTemplate') ) {
+ $templates->LimitToQueue( $self->id );
+ }
-sub WatchersAsString {
- my $self=shift;
- return($self->Watchers->EmailsAsString());
+ return ($templates);
}
-# }}}
-# {{{ sub AdminCcAsString
-=head2 AdminCcAsString
-Takes nothing. returns a string: All Ticket/Queue AdminCcs.
+=head2 CustomField NAME
-=cut
+Load the queue-specific custom field named NAME
+=cut
-sub AdminCcAsString {
- my $self=shift;
-
- return($self->AdminCc->EmailsAsString());
- }
+sub CustomField {
+ my $self = shift;
+ my $name = shift;
+ my $cf = RT::CustomField->new($self->CurrentUser);
+ $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id);
+ return ($cf);
+}
-# }}}
-# {{{ sub CcAsString
-=head2 CcAsString
+=head2 TicketCustomFields
-B<Returns> String: All Queue Ccs as a comma delimited set of email addresses.
+Returns an L<RT::CustomFields> object containing all global and
+queue-specific B<ticket> custom fields.
=cut
-sub CcAsString {
- my $self=shift;
-
- return ($self->Cc->EmailsAsString());
+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);
}
-# }}}
-# {{{ sub Cc
-=head2 Cc
+=head2 TicketTransactionCustomFields
-Takes nothing.
-Returns a watchers object which contains this queue\'s Cc watchers
+Returns an L<RT::CustomFields> object containing all global and
+queue-specific B<transaction> custom fields.
=cut
-sub Cc {
+sub TicketTransactionCustomFields {
my $self = shift;
- my $cc = $self->Watchers();
- if ($self->CurrentUserHasRight('SeeQueue')) {
- $cc->LimitToCc();
+
+ my $cfs = RT::CustomFields->new( $self->CurrentUser );
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $cfs->SetContextObject( $self );
+ $cfs->LimitToGlobalOrObjectId( $self->Id );
+ $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' );
+ $cfs->ApplySortOrder;
}
- return ($cc);
+ return ($cfs);
}
-# A helper function for Cc, so that we can call it from the ACL checks
-# without going through acl checks.
-sub _Cc {
- my $self = shift;
- my $cc = $self->Watchers();
- $cc->LimitToCc();
- return($cc);
-
-}
-# }}}
-# {{{ sub AdminCc
-=head2 AdminCc
+=head2 AllRoleGroupTypes
-Takes nothing.
-Returns this queue's administrative Ccs as an RT::Watchers object
+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 AdminCc {
+sub AllRoleGroupTypes {
my $self = shift;
- my $admin_cc = $self->Watchers();
- if ($self->CurrentUserHasRight('SeeQueue')) {
- $admin_cc->LimitToAdminCc();
- }
- return($admin_cc);
+ return ($self->ManageableRoleGroupTypes, qw(Requestor Owner));
}
-#helper function for AdminCc so we can call it without ACLs
-sub _AdminCc {
+=head2 IsRoleGroupType
+
+Returns whether the passed-in type is a role group type.
+
+=cut
+
+sub IsRoleGroupType {
my $self = shift;
- my $admin_cc = $self->Watchers();
- $admin_cc->LimitToAdminCc();
- return($admin_cc);
+ my $type = shift;
+
+ for my $valid_type ($self->AllRoleGroupTypes) {
+ return 1 if $type eq $valid_type;
+ }
+
+ return 0;
}
-# }}}
+=head2 ManageableRoleGroupTypes
-# {{{ IsWatcher, IsCc, IsAdminCc
+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>.
-# {{{ sub IsWatcher
+=cut
-# a generic routine to be called by IsRequestor, IsCc and IsAdminCc
+sub ManageableRoleGroupTypes {
+ return qw(Cc AdminCc);
+}
-=head2 IsWatcher
+=head2 IsManageableRoleGroupType
-Takes a param hash with the attributes Type and User. User is either a user object or string containing an email address. Returns true if that user or string
-is a queue watcher. Returns undef otherwise
+Returns whether the passed-in type is a manageable role group type.
=cut
-sub IsWatcher {
+sub IsManageableRoleGroupType {
my $self = shift;
-
- my %args = ( Type => 'Requestor',
- Id => undef,
- Email => undef,
- @_
- );
- #ACL check - can't do it. we need this method for ACL checks
- # unless ($self->CurrentUserHasRight('SeeQueue')) {
- # return(undef);
- # }
-
-
- my %cols = ('Type' => $args{'Type'},
- 'Scope' => 'Queue',
- 'Value' => $self->Id
- );
- if (defined ($args{'Id'})) {
- if (ref($args{'Id'})){ #If it's a ref, assume it's an RT::User object;
- #Dangerous but ok for now
- $cols{'Owner'} = $args{'Id'}->Id;
- }
- elsif ($args{'Id'} =~ /^\d+$/) { # if it's an integer, it's an RT::User obj
- $cols{'Owner'} = $args{'Id'};
- }
- else {
- $cols{'Email'} = $args{'Id'};
- }
- }
-
- if (defined $args{'Email'}) {
- $cols{'Email'} = $args{'Email'};
- }
+ my $type = shift;
- my ($description);
- $description = join(":",%cols);
-
- #If we've cached a positive match...
- if (defined $self->{'watchers_cache'}->{"$description"}) {
- if ($self->{'watchers_cache'}->{"$description"} == 1) {
- return(1);
- }
- #If we've cached a negative match...
- else {
- return(undef);
- }
- }
-
- require RT::Watcher;
- my $watcher = new RT::Watcher($self->CurrentUser);
- $watcher->LoadByCols(%cols);
-
-
- if ($watcher->id) {
- $self->{'watchers_cache'}->{"$description"} = 1;
- return(1);
- }
- else {
- $self->{'watchers_cache'}->{"$description"} = 0;
- return(undef);
+ for my $valid_type ($self->ManageableRoleGroupTypes) {
+ return 1 if $type eq $valid_type;
}
-
+
+ return 0;
}
-# }}}
-# {{{ sub IsCc
+=head2 _CreateQueueGroups
-=head2 IsCc
+Create the ticket groups and links for this ticket.
+This routine expects to be called from Ticket->Create _inside of a transaction_
-Takes a string. Returns true if the string is a Cc watcher of the current queue
+It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
-=item Bugs
+It will return true on success and undef on failure.
-Should also be able to handle an RT::User object
=cut
+sub _CreateQueueGroups {
+ my $self = shift;
-sub IsCc {
- my $self = shift;
- my $cc = shift;
-
- return ($self->IsWatcher( Type => 'Cc', Id => $cc ));
-
+ 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;
-# {{{ sub IsAdminCc
+ 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);
+ }
-=head2 IsAdminCc
+ return $id;
+}
-Takes a string. Returns true if the string is an AdminCc watcher of the current queue
-=item Bugs
-Should also be able to handle an RT::User object
+# _HasModifyWatcherRight {{{
+sub _HasModifyWatcherRight {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
-=cut
+ return 1 if $self->CurrentUserHasRight('ModifyQueueWatchers');
-sub IsAdminCc {
- my $self = shift;
- my $admincc = shift;
-
- return ($self->IsWatcher( Type => 'AdminCc', Id => $admincc ));
-
+ #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
-# {{{ sub AddWatcher
+AddWatcher takes a parameter hash. The keys are as follows:
-=head2 AddWatcher
+Type One of Requestor, Cc, AdminCc
-Takes a paramhash of Email, Owner and Type. Type is one of 'Cc' or 'AdminCc',
-We need either an Email Address in Email or a userid in Owner
+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 = ( Email => undef,
- Type => undef,
- Owner => 0,
- @_
- );
-
- # {{{ Check ACLS
- #If the watcher we're trying to add is for the current user
- if ( ( ( defined $args{'Email'}) &&
- ( $args{'Email'} eq $self->CurrentUser->EmailAddress) ) or
- ($args{'Owner'} eq $self->CurrentUser->Id)) {
-
- # If it's an AdminCc and they don't have
- # 'WatchAsAdminCc' or 'ModifyQueueWatchers', bail
- if ($args{'Type'} eq 'AdminCc') {
- unless ($self->CurrentUserHasRight('ModifyQueueWatchers') or
- $self->CurrentUserHasRight('WatchAsAdminCc')) {
- return(0, 'Permission Denied');
- }
- }
-
- # If it's a Requestor or Cc and they don't have
- # 'Watch' or 'ModifyQueueWatchers', bail
- elsif ($args{'Type'} eq 'Cc') {
- unless ($self->CurrentUserHasRight('ModifyQueueWatchers') or
- $self->CurrentUserHasRight('Watch')) {
- return(0, 'Permission Denied');
- }
- }
- else {
- $RT::Logger->warn("$self -> AddWatcher hit code".
- " it never should. We got passed ".
- " a type of ". $args{'Type'});
- return (0,'Error in parameters to $self AddWatcher');
- }
- }
- # If the watcher isn't the current user
- # and the current user doesn't have 'ModifyQueueWatchers'
- # bail
- else {
- unless ($self->CurrentUserHasRight('ModifyQueueWatchers')) {
- return (0, "Permission Denied");
- }
+ 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;
}
- # }}}
-
- require RT::Watcher;
- my $Watcher = new RT::Watcher ($self->CurrentUser);
- return ($Watcher->Create(Scope => 'Queue',
- Value => $self->Id,
- Email => $args{'Email'},
- Type => $args{'Type'},
- Owner => $args{'Owner'}
- ));
+
+ 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"));
+ }
-# {{{ sub AddCc
+ 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"));
+ }
-=head2 AddCc
+ if ( $group->HasMember( $principal)) {
-Add a Cc to this queue.
-Takes a paramhash of Email and Owner.
-We need either an Email Address in Email or a userid in Owner
+ return ( 0, $self->loc('[_1] is already a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
+ }
-=cut
+ 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);
-sub AddCc {
- my $self = shift;
- return ($self->AddWatcher( Type => 'Cc', @_));
+ return ( 0, $self->loc('Could not make [_1] a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
+ }
+ return ( 1, $self->loc("Added [_1] to members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
}
-# }}}
-# {{{ sub AddAdminCc
-=head2 AddAdminCc
-Add an Administrative Cc to this queue.
-Takes a paramhash of Email and Owner.
-We need either an Email Address in Email or a userid in Owner
+=head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
-=cut
-sub AddAdminCc {
- my $self = shift;
- return ($self->AddWatcher( Type => 'AdminCc', @_));
-}
-# }}}
+Deletes a queue watcher. Takes two arguments:
-# {{{ sub DeleteWatcher
+Type (one of Requestor,Cc,AdminCc)
-=head2 DeleteWatcher id [type]
+and one of
-DeleteWatcher takes a single argument which is either an email address
-or a watcher id.
-If the first argument is an email address, you need to specify the watcher type you're talking
-about as the second argument. Valid values are 'Cc' or 'AdminCc'.
-It removes that watcher from this Queue\'s list of watchers.
+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 $id = shift;
-
- my $type;
-
- $type = shift if (@_);
-
- require RT::Watcher;
- my $Watcher = new RT::Watcher($self->CurrentUser);
-
- #If it\'s a numeric watcherid
- if ($id =~ /^(\d*)$/) {
- $Watcher->Load($id);
+ my %args = ( Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_ );
+
+ unless ( $args{'PrincipalId'} || $args{'Email'} ) {
+ return ( 0, $self->loc("No principal specified") );
}
-
- #Otherwise, we'll assume it's an email address
- elsif ($type) {
- my ($result, $msg) =
- $Watcher->LoadByValue( Email => $id,
- Scope => 'Queue',
- Value => $self->id,
- Type => $type);
- return (0,$msg) unless ($result);
+
+ 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;
}
- else {
- return(0,"Can\'t delete a watcher by email address without specifying a type");
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ if ( $args{'PrincipalId'} ) {
+ $principal->Load( $args{'PrincipalId'} );
}
-
- # {{{ Check ACLS
-
- #If the watcher we're trying to delete is for the current user
- if ($Watcher->Email eq $self->CurrentUser->EmailAddress) {
-
- # If it's an AdminCc and they don't have
- # 'WatchAsAdminCc' or 'ModifyQueueWatchers', bail
- if ($Watcher->Type eq 'AdminCc') {
- unless ($self->CurrentUserHasRight('ModifyQueueWatchers') or
- $self->CurrentUserHasRight('WatchAsAdminCc')) {
- return(0, 'Permission Denied');
- }
- }
-
- # If it's a Cc and they don't have
- # 'Watch' or 'ModifyQueueWatchers', bail
- elsif ($Watcher->Type eq 'Cc') {
- unless ($self->CurrentUserHasRight('ModifyQueueWatchers') or
- $self->CurrentUserHasRight('Watch')) {
- return(0, 'Permission Denied');
- }
- }
- else {
- $RT::Logger->warn("$self -> DeleteWatcher hit code".
- " it never should. We got passed ".
- " a type of ". $args{'Type'});
- return (0,'Error in parameters to $self DeleteWatcher');
- }
- }
- # If the watcher isn't the current user
- # and the current user doesn't have 'ModifyQueueWatchers'
- # bail
else {
- unless ($self->CurrentUserHasRight('ModifyQueueWatchers')) {
- return (0, "Permission Denied");
- }
+ my $user = RT::User->new( $self->CurrentUser );
+ $user->LoadByEmail( $args{'Email'} );
+ $principal->Load( $user->Id );
}
- # }}}
-
- unless (($Watcher->Scope eq 'Queue') and
- ($Watcher->Value == $self->id) ) {
- return (0, "Not a watcher for this queue");
+ # If we can't find this watcher, we need to bail.
+ unless ( $principal->Id ) {
+ return ( 0, $self->loc("Could not find that principal") );
}
-
- #Clear out the watchers hash.
- $self->{'watchers'} = undef;
-
- my $retval = $Watcher->Delete();
-
- unless ($retval) {
- return(0,"Watcher could not be deleted.");
+ 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(1, "Watcher deleted");
-}
-# {{{ sub DeleteCc
+ return ( 0, $self->loc('Unknown watcher type [_1]', $args{Type}) )
+ unless $self->IsRoleGroupType($args{Type});
-=head2 DeleteCc EMAIL
+ my ($ok, $msg) = $self->_HasModifyWatcherRight(%args);
+ return ($ok, $msg) if !$ok;
-Takes an email address. It calls DeleteWatcher with a preset
-type of 'Cc'
+ # see if this user is already a watcher.
+ unless ( $group->HasMember($principal)) {
+ return ( 0, $self->loc('[_1] is not a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
+ }
-=cut
+ 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);
-sub DeleteCc {
- my $self = shift;
- my $id = shift;
- return ($self->DeleteWatcher ($id, 'Cc'))
-}
+ return ( 0, $self->loc('Could not remove [_1] as a [_2] for this queue',
+ $principal->Object->Name, $args{'Type'}) );
+ }
-# }}}
+ return ( 1, $self->loc("Removed [_1] from members of [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
+}
-# {{{ sub DeleteAdminCc
-=head2 DeleteAdminCc EMAIL
-Takes an email address. It calls DeleteWatcher with a preset
-type of 'AdminCc'
+=head2 AdminCcAddresses
+returns String: All queue AdminCc email addresses as a string
=cut
-sub DeleteAdminCc {
- my $self = shift;
- my $id = shift;
- return ($self->DeleteWatcher ($id, 'AdminCc'))
-}
+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
-# {{{ Dealing with keyword selects
+=cut
-# {{{ sub AddKeywordSelect
+sub CcAddresses {
+ my $self = shift;
-=head2 AddKeywordSelect
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return undef;
+ }
+
+ return ( $self->Cc->MemberEmailAddressesAsString);
+
+}
-Takes a paramhash of Name, Keyword, Depth and Single. Adds a new KeywordSelect for
-this queue with those attributes.
-=cut
+=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 AddKeywordSelect {
+sub Cc {
my $self = shift;
- my %args = ( Keyword => undef,
- Depth => undef,
- Single => undef,
- Name => undef,
- @_);
-
- #ACLS get handled in KeywordSelect
- my $NewKeywordSelect = new RT::KeywordSelect($self->CurrentUser);
-
- return ($NewKeywordSelect->Create (Keyword => $args{'Keyword'},
- Depth => $args{'Depth'},
- Name => $args{'Name'},
- Single => $args{'Single'},
- ObjectType => 'Ticket',
- ObjectField => 'Queue',
- ObjectValue => $self->Id()
- ) );
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
+ }
+ return ($group);
+
}
-# }}}
-# {{{ sub KeywordSelect
-=head2 KeywordSelect([NAME])
+=head2 AdminCc
-Takes the name of a keyword select for this queue or that's global.
-Returns the relevant KeywordSelect object. Prefers a keywordselect that's
-specific to this queue over a global one. If it can't find the proper
-Keword select or the user doesn't have permission, returns an empty
-KeywordSelect object
+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 KeywordSelect {
+sub AdminCc {
my $self = shift;
- my $name = shift;
-
- require RT::KeywordSelect;
- my $select = RT::KeywordSelect->new($self->CurrentUser);
- if ($self->CurrentUserHasRight('SeeQueue')) {
- $select->LoadByName( Name => $name, Queue => $self->Id);
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
}
- return ($select);
+ return ($group);
+
}
-# }}}
-# {{{ sub KeywordSelects
+# 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
-=head2 KeywordSelects
+PrincipalId is an RT::Principal id
+
+Returns true if that principal is a member of the group Type for this queue
-Returns an B<RT::KeywordSelects> object containing the collection of
-B<RT::KeywordSelect> objects which apply to this queue. (Both queue specific keyword selects
-and global keyword selects.
=cut
-sub KeywordSelects {
- my $self = shift;
+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
- use RT::KeywordSelects;
- my $KeywordSelects = new RT::KeywordSelects($self->CurrentUser);
+ my $principal = RT::Principal->new($self->CurrentUser);
+ $principal->Load($args{'PrincipalId'});
+ unless ($principal->Id) {
+ return (undef);
+ }
- if ($self->CurrentUserHasRight('SeeQueue')) {
- $KeywordSelects->LimitToQueue($self->id);
- $KeywordSelects->IncludeGlobals();
- }
- return ($KeywordSelects);
+ return ($group->HasMemberRecursively($principal));
}
-# }}}
-# }}}
-# {{{ ACCESS CONTROL
-# {{{ sub ACL
-=head2 ACL
+=head2 IsCc PRINCIPAL_ID
+
+Takes an RT::Principal id.
+Returns true if the principal is a requestor of the current queue.
-#Returns an RT::ACL object of ACEs everyone who has anything to do with this queue.
=cut
-sub ACL {
+sub IsCc {
my $self = shift;
-
- use RT::ACL;
- my $acl = new RT::ACL($self->CurrentUser);
-
- if ($self->CurrentUserHasRight('ShowACL')) {
- $acl->LimitToQueue($self->Id);
- }
-
- return ($acl);
+ 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
+
+
+
+
+
+
+
+
sub _Set {
my $self = shift;
- unless ($self->CurrentUserHasRight('AdminQueue')) {
- return(0, 'Permission Denied');
- }
- return ($self->SUPER::_Set(@_));
+ unless ( $self->CurrentUserHasRight('AdminQueue') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ RT->System->QueueCacheNeedsUpdate(1);
+ return ( $self->SUPER::_Set(@_) );
}
-# }}}
-# {{{ sub _Value
+
sub _Value {
my $self = shift;
- unless ($self->CurrentUserHasRight('SeeQueue')) {
- return (undef);
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return (undef);
}
- return ($self->__Value(@_));
+ return ( $self->__Value(@_) );
}
-# }}}
-# {{{ sub CurrentUserHasRight
=head2 CurrentUserHasRight
=cut
sub CurrentUserHasRight {
- my $self = shift;
- my $right = shift;
+ my $self = shift;
+ my $right = shift;
- return ($self->HasRight( Principal=> $self->CurrentUser,
- Right => "$right"));
+ return (
+ $self->HasRight(
+ Principal => $self->CurrentUser,
+ Right => "$right"
+ )
+ );
}
-# }}}
+=head2 CurrentUserCanSee
+
+Returns true if the current user can see the queue, using SeeQueue
+
+=cut
+
+sub CurrentUserCanSee {
+ my $self = shift;
+
+ return $self->CurrentUserHasRight('SeeQueue');
+}
-# {{{ sub HasRight
=head2 HasRight
# TAKES: Right and optional "Principal" which defaults to the current user
sub HasRight {
my $self = shift;
- my %args = ( Right => undef,
- Principal => $self->CurrentUser,
- @_);
- unless(defined $args{'Principal'}) {
- $RT::Logger->debug("Principal undefined in Queue::HasRight");
+ 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($args{'Principal'}->HasQueueRight(QueueObj => $self,
- Right => $args{'Right'}));
+ return $principal->HasRight(
+ %args,
+ Object => ($self->Id ? $self : $RT::System),
+ );
}
-# }}}
-# }}}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 CorrespondAddress
+
+Returns the current value of CorrespondAddress.
+(In the database, CorrespondAddress is stored as varchar(120).)
+
+
+
+=head2 SetCorrespondAddress VALUE
+
+
+Set CorrespondAddress to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CorrespondAddress will be stored as a varchar(120).)
+
+
+=cut
+
+
+=head2 CommentAddress
+
+Returns the current value of CommentAddress.
+(In the database, CommentAddress is stored as varchar(120).)
+
+
+
+=head2 SetCommentAddress VALUE
+
+
+Set CommentAddress to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CommentAddress will be stored as a varchar(120).)
+
+
+=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.
+(In the database, InitialPriority is stored as int(11).)
+
+
+
+=head2 SetInitialPriority VALUE
+
+
+Set InitialPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, InitialPriority will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 FinalPriority
+
+Returns the current value of FinalPriority.
+(In the database, FinalPriority is stored as int(11).)
+
+
+
+=head2 SetFinalPriority VALUE
+
+
+Set FinalPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, FinalPriority will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 DefaultDueIn
+
+Returns the current value of DefaultDueIn.
+(In the database, DefaultDueIn is stored as int(11).)
+
+
+
+=head2 SetDefaultDueIn VALUE
+
+
+Set DefaultDueIn to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, DefaultDueIn will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+=head2 Disabled
+
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
+
+
+
+=head2 SetDisabled VALUE
+
+
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {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 => ''},
+ Description =>
+ {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 => ''},
+ CommentAddress =>
+ {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 => 'default'},
+ InitialPriority =>
+ {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'},
+ DefaultDueIn =>
+ {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'},
+ Created =>
+ {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'},
+ LastUpdated =>
+ {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'},
+
+ }
+};
+
+
+
+RT::Base->_ImportOverlays();
1;