X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FQueue.pm;h=a942bb6d74b8d08d877d375f3ac7ad74923213e2;hb=a72a10f754f7465121d6137bb3dcee0a21ea6443;hp=1656903b36dc9f1c0b226e110df2dcba3d0d41a3;hpb=0ebeec96313dd7edfca340f01f8fbbbac1f4aa1d;p=freeside.git diff --git a/rt/lib/RT/Queue.pm b/rt/lib/RT/Queue.pm index 1656903b3..a942bb6d7 100755 --- a/rt/lib/RT/Queue.pm +++ b/rt/lib/RT/Queue.pm @@ -1,4 +1,50 @@ -# $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-2012 Best Practical Solutions, LLC +# +# +# (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 @@ -10,185 +56,471 @@ =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, C [, ...] + +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, C [, ...] -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; + + if ( $value && $value ne 'default' ) { + return (0, $self->loc('[_1] is not valid lifecycle', $value )) + unless $self->ValidateLifecycle( $value ); + } else { + $value = undef; + } -=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; +} + - my $retval = grep (/^$value$/, $self->StatusArray); - return ($retval); +=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 _Init -sub _Init { +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 + +Returns true if value is a valid status. Otherwise, returns 0. + +=cut + +sub IsValidStatus { + my $self = shift; + return $self->Lifecycle->IsValid( shift ); +} -# {{{ sub Create +=head2 IsActiveStatus value -=head2 Create +Returns true if value is a Active status. Otherwise, returns 0 -Create takes the name of the new queue +=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; } + + 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 - - 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') ); } - return ($id, "Queue $id 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; + } + + 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 @@ -200,423 +532,472 @@ a new queue. Returns undef if there's already a queue by that 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 + my $templates = RT::Templates->new( $self->CurrentUser ); -Returns a string of all queue watchers email addresses concatenated with ','s. - -=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 String: All Queue Ccs as a comma delimited set of email addresses. +Returns an L object containing all global and +queue-specific B 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 object containing all global and +queue-specific B 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. =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. -# {{{ 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; + + my @types = $self->AllRoleGroupTypes; -sub IsCc { - my $self = shift; - my $cc = shift; - - return ($self->IsWatcher( Type => 'Cc', Id => $cc )); - + 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 + +AddWatcher takes a parameter hash. The keys are as follows: -# {{{ sub AddWatcher +Type One of Requestor, Cc, AdminCc -=head2 AddWatcher +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. -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 +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('That principal is already a [_1] for this queue', $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 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'} )); } -# }}} -# {{{ 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 @@ -624,275 +1005,253 @@ It removes that watcher from this Queue\'s list of watchers. 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}); + + my ($ok, $msg) = $self->_HasModifyWatcherRight(%args); + return ($ok, $msg) if !$ok; -=head2 DeleteCc EMAIL + # see if this user is already a watcher. -Takes an email address. It calls DeleteWatcher with a preset -type of 'Cc' + 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); -=cut + return ( 0, $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) ); + } -sub DeleteCc { - my $self = shift; - my $id = shift; - return ($self->DeleteWatcher ($id, 'Cc')) + 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; + } -Takes a paramhash of Name, Keyword, Depth and Single. Adds a new KeywordSelect for -this queue with those attributes. + return ( $self->Cc->MemberEmailAddressesAsString); + +} -=cut -sub AddKeywordSelect { +=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 %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 + +PrincipalId is an RT::Principal id -=head2 KeywordSelects +Returns true if that principal is a member of the group Type for this queue -Returns an B object containing the collection of -B 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, + @_ + ); - use RT::KeywordSelects; - my $KeywordSelects = new RT::KeywordSelects($self->CurrentUser); + # 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); + } - 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') ); + } + 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 @@ -903,17 +1262,30 @@ Returns undef otherwise. =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 @@ -927,18 +1299,290 @@ Returns undef otherwise. # 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 => ''}, + 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;