X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FScrips.pm;h=193399d8ca032e548236f3be149db17776efbd94;hb=7322f2afedcc2f427e997d1535a503613a83f088;hp=13a4b7d7d5946381eb625150d1eb462c257001bc;hpb=43a06151e47d2c59b833cbd8c26d97865ee850b6;p=freeside.git diff --git a/rt/lib/RT/Scrips.pm b/rt/lib/RT/Scrips.pm index 13a4b7d7d..193399d8c 100755 --- a/rt/lib/RT/Scrips.pm +++ b/rt/lib/RT/Scrips.pm @@ -2,7 +2,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -69,12 +69,20 @@ package RT::Scrips; use strict; use warnings; -use RT::Scrip; - use base 'RT::SearchBuilder'; +use RT::Scrip; +use RT::ObjectScrips; + sub Table { 'Scrips'} +sub _Init { + my $self = shift; + + $self->{'with_disabled_column'} = 1; + + return ( $self->SUPER::_Init(@_) ); +} =head2 LimitToQueue @@ -85,14 +93,17 @@ another call to this method =cut sub LimitToQueue { - my $self = shift; - my $queue = shift; - - $self->Limit (ENTRYAGGREGATOR => 'OR', - FIELD => 'Queue', - VALUE => "$queue") - if defined $queue; - + my $self = shift; + my $queue = shift; + return unless defined $queue; + + my $alias = RT::ObjectScrips->new( $self->CurrentUser ) + ->JoinTargetToThis( $self ); + $self->Limit( + ALIAS => $alias, + FIELD => 'ObjectId', + VALUE => int $queue, + ); } @@ -106,43 +117,140 @@ another call to this method or LimitToQueue sub LimitToGlobal { - my $self = shift; - - $self->Limit (ENTRYAGGREGATOR => 'OR', - FIELD => 'Queue', - VALUE => 0); - + my $self = shift; + return $self->LimitToQueue(0); +} + +sub LimitToAdded { + my $self = shift; + return RT::ObjectScrips->new( $self->CurrentUser ) + ->LimitTargetToAdded( $self => @_ ); } -# {{{ sub Next +sub LimitToNotAdded { + my $self = shift; + return RT::ObjectScrips->new( $self->CurrentUser ) + ->LimitTargetToNotAdded( $self => @_ ); +} + +sub LimitByStage { + my $self = shift; + my %args = @_%2? (Stage => @_) : @_; + return unless defined $args{'Stage'}; + + my $alias = RT::ObjectScrips->new( $self->CurrentUser ) + ->JoinTargetToThis( $self, %args ); + $self->Limit( + ALIAS => $alias, + FIELD => 'Stage', + VALUE => $args{'Stage'}, + ); +} -=head2 Next +=head2 LimitByTemplate -Returns the next scrip that this user can see. +Takes a L object and limits scrips to those that +use the template. =cut - -sub Next { + +sub LimitByTemplate { my $self = shift; - - - my $Scrip = $self->SUPER::Next(); - if ((defined($Scrip)) and (ref($Scrip))) { - - if ($Scrip->CurrentUserHasRight('ShowScrips')) { - return($Scrip); - } - - #If the user doesn't have the right to show this scrip - else { - return($self->Next()); - } + my $template = shift; + + $self->Limit( FIELD => 'Template', VALUE => $template->Name ); + + if ( $template->Queue ) { + # if template is local then we are interested in global and + # queue specific scrips + $self->LimitToQueue( $template->Queue ); + $self->LimitToGlobal; } - #if there never was any scrip - else { - return(undef); - } - + else { # template is global + + # if every queue has a custom version then there + # is no scrip that uses the template + { + my $queues = RT::Queues->new( RT->SystemUser ); + my $alias = $queues->Join( + TYPE => 'LEFT', + ALIAS1 => 'main', + FIELD1 => 'id', + TABLE2 => 'Templates', + FIELD2 => 'Queue', + ); + $queues->Limit( + LEFTJOIN => $alias, + ALIAS => $alias, + FIELD => 'Name', + VALUE => $template->Name, + ); + $queues->Limit( + ALIAS => $alias, + FIELD => 'id', + OPERATOR => 'IS', + VALUE => 'NULL', + ); + return $self->Limit( FIELD => 'id', VALUE => 0 ) + unless $queues->Count; + } + + # otherwise it's either a global scrip or application to + # a queue with custom version of the template. + my $os_alias = RT::ObjectScrips->new( $self->CurrentUser ) + ->JoinTargetToThis( $self ); + my $tmpl_alias = $self->Join( + TYPE => 'LEFT', + ALIAS1 => $os_alias, + FIELD1 => 'ObjectId', + TABLE2 => 'Templates', + FIELD2 => 'Queue', + ); + $self->Limit( + LEFTJOIN => $tmpl_alias, ALIAS => $tmpl_alias, FIELD => 'Name', VALUE => $template->Name, + ); + $self->Limit( + LEFTJOIN => $tmpl_alias, ALIAS => $tmpl_alias, FIELD => 'Queue', OPERATOR => '!=', VALUE => 0, + ); + + $self->_OpenParen('UsedBy'); + $self->Limit( SUBCLAUSE => 'UsedBy', ALIAS => $os_alias, FIELD => 'ObjectId', VALUE => 0 ); + $self->Limit( + SUBCLAUSE => 'UsedBy', + ALIAS => $tmpl_alias, + FIELD => 'id', + OPERATOR => 'IS', + VALUE => 'NULL', + ); + $self->_CloseParen('UsedBy'); + } +} + +sub ApplySortOrder { + my $self = shift; + my $order = shift || 'ASC'; + $self->OrderByCols( { + ALIAS => RT::ObjectScrips->new( $self->CurrentUser ) + ->JoinTargetToThis( $self => @_ ) + , + FIELD => 'SortOrder', + ORDER => $order, + } ); +} + +=head2 AddRecord + +Overrides the collection to ensure that only scrips the user can see are +returned. + +=cut + +sub AddRecord { + my $self = shift; + my ($record) = @_; + + return unless $record->CurrentUserHasRight('ShowScrips'); + return $self->SUPER::AddRecord( $record ); } =head2 Apply @@ -178,16 +286,6 @@ Commit all of this object's prepared scrips sub Commit { my $self = shift; - # RT::Scrips->_SetupSourceObjects will clobber - # the CurrentUser, but we need to keep this ticket - # so that the _TransactionBatch cache is maintained - # and doesn't run twice. sigh. - $self->_StashCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj}; - - #We're really going to need a non-acled ticket for the scrips to work - $self->_SetupSourceObjects( TicketObj => $self->{'TicketObj'}, - TransactionObj => $self->{'TransactionObj'} ); - foreach my $scrip (@{$self->Prepared}) { $RT::Logger->debug( "Committing scrip #". $scrip->id @@ -199,8 +297,6 @@ sub Commit { TransactionObj => $self->{'TransactionObj'} ); } - # Apply the bandaid. - $self->_RestoreCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj}; } @@ -221,12 +317,6 @@ sub Prepare { Type => undef, @_ ); - # RT::Scrips->_SetupSourceObjects will clobber - # the CurrentUser, but we need to keep this ticket - # so that the _TransactionBatch cache is maintained - # and doesn't run twice. sigh. - $self->_StashCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj}; - #We're really going to need a non-acled ticket for the scrips to work $self->_SetupSourceObjects( TicketObj => $args{'TicketObj'}, Ticket => $args{'Ticket'}, @@ -259,10 +349,6 @@ sub Prepare { } - # Apply the bandaid. - $self->_RestoreCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj}; - - return (@{$self->Prepared}); }; @@ -279,40 +365,6 @@ sub Prepared { return ($self->{'prepared_scrips'} || []); } -=head2 _StashCurrentUser TicketObj => RT::Ticket - -Saves aside the current user of the original ticket that was passed to these scrips. -This is used to make sure that we don't accidentally leak the RT_System current user -back to the calling code. - -=cut - -sub _StashCurrentUser { - my $self = shift; - my %args = @_; - - $self->{_TicketCurrentUser} = $args{TicketObj}->CurrentUser; -} - -=head2 _RestoreCurrentUser TicketObj => RT::Ticket - -Uses the current user saved by _StashCurrentUser to reset a Ticket object -back to the caller's current user and avoid leaking an RT_System ticket to -calling code. - -=cut - -sub _RestoreCurrentUser { - my $self = shift; - my %args = @_; - unless ( $self->{_TicketCurrentUser} ) { - RT->Logger->debug("Called _RestoreCurrentUser without a stashed current user object"); - return; - } - $args{TicketObj}->CurrentUser($self->{_TicketCurrentUser}); - -} - =head2 _SetupSourceObjects { TicketObj , Ticket, Transaction, TransactionObj } Setup a ticket and transaction for this Scrip collection to work with as it runs through the @@ -334,14 +386,22 @@ sub _SetupSourceObjects { @_ ); - if ( $self->{'TicketObj'} = $args{'TicketObj'} ) { - # This clobbers the passed in TicketObj by turning it into one - # whose current user is RT_System. Anywhere in the Web UI - # currently calling into this is thus susceptable to a privilege - # leak; the only current call site is ->Apply, which bandaids - # over the top of this by re-asserting the CurrentUser - # afterwards. - $self->{'TicketObj'}->CurrentUser( $self->CurrentUser ); + if ( $args{'TicketObj'} ) { + # This loads a clean copy of the Ticket object to ensure that we + # don't accidentally escalate the privileges of the passed in + # ticket (this function can be invoked from the UI). + # We copy the TransactionBatch transactions so that Scrips + # running against the new Ticket will have access to them. We + # use RanTransactionBatch to guard against running + # TransactionBatch Scrips more than once. + $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser ); + $self->{'TicketObj'}->Load( $args{'TicketObj'}->Id ); + if ( $args{'TicketObj'}->TransactionBatch ) { + # try to ensure that we won't infinite loop if something dies, triggering DESTROY while + # we have the _TransactionBatch objects; + $self->{'TicketObj'}->RanTransactionBatch(1); + $self->{'TicketObj'}->{'_TransactionBatch'} = $args{'TicketObj'}->{'_TransactionBatch'}; + } } else { $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser ); @@ -363,10 +423,8 @@ sub _SetupSourceObjects { =head2 _FindScrips -Find only the apropriate scrips for whatever we're doing now. Order them -by their description. (Most common use case is to prepend a number to the -description, forcing the scrips to display and run in ascending alphanumerical -order.) +Find only the appropriate scrips for whatever we're doing now. Order +them by the SortOrder field from the ObjectScrips table. =cut @@ -378,32 +436,27 @@ sub _FindScrips { @_ ); - $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id ) - ; #Limit it to $Ticket->QueueObj->Id - $self->LimitToGlobal(); - # or to "global" - - $self->Limit( FIELD => "Stage", VALUE => $args{'Stage'} ); - - my $ConditionsAlias = $self->NewAlias('ScripConditions'); + $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id ); + $self->LimitToGlobal; + $self->LimitByStage( $args{'Stage'} ); - $self->Join( + my $ConditionsAlias = $self->Join( ALIAS1 => 'main', FIELD1 => 'ScripCondition', - ALIAS2 => $ConditionsAlias, - FIELD2 => 'id' + TABLE2 => 'ScripConditions', + FIELD2 => 'id', ); #We only want things where the scrip applies to this sort of transaction # TransactionBatch stage can define list of transaction foreach( split /\s*,\s*/, ($args{'Type'} || '') ) { - $self->Limit( - ALIAS => $ConditionsAlias, - FIELD => 'ApplicableTransTypes', - OPERATOR => 'LIKE', - VALUE => $_, - ENTRYAGGREGATOR => 'OR', - ) + $self->Limit( + ALIAS => $ConditionsAlias, + FIELD => 'ApplicableTransTypes', + OPERATOR => 'LIKE', + VALUE => $_, + ENTRYAGGREGATOR => 'OR', + ) } # Or where the scrip applies to any transaction @@ -415,8 +468,7 @@ sub _FindScrips { ENTRYAGGREGATOR => 'OR', ); - # Promise some kind of ordering - $self->OrderBy( FIELD => 'Description' ); + $self->ApplySortOrder; # we call Count below, but later we always do search # so just do search and get count from results @@ -430,19 +482,6 @@ sub _FindScrips { ); } - - - -=head2 NewItem - -Returns an empty new RT::Scrip item - -=cut - -sub NewItem { - my $self = shift; - return(RT::Scrip->new($self->CurrentUser)); -} RT::Base->_ImportOverlays(); 1;