1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
51 RT::Scrips - a collection of RT Scrip objects
70 no warnings qw(redefine);
72 # {{{ sub LimitToQueue
76 Takes a queue id (numerical) as its only argument. Makes sure that
77 Scopes it pulls out apply to this queue (or another that you've selected with
78 another call to this method
86 $self->Limit (ENTRYAGGREGATOR => 'OR',
94 # {{{ sub LimitToGlobal
99 Scopes it pulls out apply to all queues (or another that you've selected with
100 another call to this method or LimitToQueue
108 $self->Limit (ENTRYAGGREGATOR => 'OR',
119 return(new RT::Scrip($self->CurrentUser));
127 Returns the next scrip that this user can see.
135 my $Scrip = $self->SUPER::Next();
136 if ((defined($Scrip)) and (ref($Scrip))) {
138 if ($Scrip->CurrentUserHasRight('ShowScrips')) {
142 #If the user doesn't have the right to show this scrip
144 return($self->Next());
147 #if there never was any scrip
157 Run through the relevant scrips. Scrips will run in order based on
158 description. (Most common use case is to prepend a number to the description,
159 forcing the scrips to run in ascending alphanumerical order.)
166 my %args = ( TicketObj => undef,
168 Transaction => undef,
169 TransactionObj => undef,
174 $self->Prepare(%args);
181 Commit all of this object's prepared scrips
188 # RT::Scrips->_SetupSourceObjects will clobber
189 # the CurrentUser, but we need to keep this ticket
190 # so that the _TransactionBatch cache is maintained
191 # and doesn't run twice. sigh.
192 $self->_StashCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj};
194 #We're really going to need a non-acled ticket for the scrips to work
195 $self->_SetupSourceObjects( TicketObj => $self->{'TicketObj'},
196 TransactionObj => $self->{'TransactionObj'} );
198 foreach my $scrip (@{$self->Prepared}) {
200 "Committing scrip #". $scrip->id
201 ." on txn #". $self->{'TransactionObj'}->id
202 ." of ticket #". $self->{'TicketObj'}->id
205 $scrip->Commit( TicketObj => $self->{'TicketObj'},
206 TransactionObj => $self->{'TransactionObj'} );
210 $self->_RestoreCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj};
216 Only prepare the scrips, returning an array of the scrips we're interested in
217 in order of preparation, not execution
223 my %args = ( TicketObj => undef,
225 Transaction => undef,
226 TransactionObj => undef,
231 # RT::Scrips->_SetupSourceObjects will clobber
232 # the CurrentUser, but we need to keep this ticket
233 # so that the _TransactionBatch cache is maintained
234 # and doesn't run twice. sigh.
235 $self->_StashCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj};
237 #We're really going to need a non-acled ticket for the scrips to work
238 $self->_SetupSourceObjects( TicketObj => $args{'TicketObj'},
239 Ticket => $args{'Ticket'},
240 TransactionObj => $args{'TransactionObj'},
241 Transaction => $args{'Transaction'} );
244 $self->_FindScrips( Stage => $args{'Stage'}, Type => $args{'Type'} );
247 #Iterate through each script and check it's applicability.
248 while ( my $scrip = $self->Next() ) {
250 unless ( $scrip->IsApplicable(
251 TicketObj => $self->{'TicketObj'},
252 TransactionObj => $self->{'TransactionObj'}
254 $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it isn't applicable");
258 #If it's applicable, prepare and commit it
259 unless ( $scrip->Prepare( TicketObj => $self->{'TicketObj'},
260 TransactionObj => $self->{'TransactionObj'}
262 $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it didn't Prepare");
265 push @{$self->{'prepared_scrips'}}, $scrip;
270 $self->_RestoreCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj};
273 return (@{$self->Prepared});
279 Returns an arrayref of the scrips this object has prepared
286 return ($self->{'prepared_scrips'} || []);
289 =head2 _StashCurrentUser TicketObj => RT::Ticket
291 Saves aside the current user of the original ticket that was passed to these scrips.
292 This is used to make sure that we don't accidentally leak the RT_System current user
293 back to the calling code.
297 sub _StashCurrentUser {
301 $self->{_TicketCurrentUser} = $args{TicketObj}->CurrentUser;
304 =head2 _RestoreCurrentUser TicketObj => RT::Ticket
306 Uses the current user saved by _StashCurrentUser to reset a Ticket object
307 back to the caller's current user and avoid leaking an RT_System ticket to
312 sub _RestoreCurrentUser {
315 unless ( $self->{_TicketCurrentUser} ) {
316 RT->Logger->debug("Called _RestoreCurrentUser without a stashed current user object");
319 $args{TicketObj}->CurrentUser($self->{_TicketCurrentUser});
323 # {{{ sup _SetupSourceObjects
325 =head2 _SetupSourceObjects { TicketObj , Ticket, Transaction, TransactionObj }
327 Setup a ticket and transaction for this Scrip collection to work with as it runs through the
328 relevant scrips. (Also to figure out which scrips apply)
335 sub _SetupSourceObjects {
341 Transaction => undef,
342 TransactionObj => undef,
346 if ( $self->{'TicketObj'} = $args{'TicketObj'} ) {
347 # This clobbers the passed in TicketObj by turning it into one
348 # whose current user is RT_System. Anywhere in the Web UI
349 # currently calling into this is thus susceptable to a privilege
350 # leak; the only current call site is ->Apply, which bandaids
351 # over the top of this by re-asserting the CurrentUser
353 $self->{'TicketObj'}->CurrentUser( $self->CurrentUser );
356 $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser );
357 $self->{'TicketObj'}->Load( $args{'Ticket'} )
358 || $RT::Logger->err("$self couldn't load ticket $args{'Ticket'}");
361 if ( ( $self->{'TransactionObj'} = $args{'TransactionObj'} ) ) {
362 $self->{'TransactionObj'}->CurrentUser( $self->CurrentUser );
365 $self->{'TransactionObj'} = RT::Transaction->new( $self->CurrentUser );
366 $self->{'TransactionObj'}->Load( $args{'Transaction'} )
367 || $RT::Logger->err( "$self couldn't load transaction $args{'Transaction'}");
373 # {{{ sub _FindScrips;
377 Find only the apropriate scrips for whatever we're doing now. Order them
378 by their description. (Most common use case is to prepend a number to the
379 description, forcing the scrips to display and run in ascending alphanumerical
392 $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id )
393 ; #Limit it to $Ticket->QueueObj->Id
394 $self->LimitToGlobal();
397 $self->Limit( FIELD => "Stage", VALUE => $args{'Stage'} );
399 my $ConditionsAlias = $self->NewAlias('ScripConditions');
403 FIELD1 => 'ScripCondition',
404 ALIAS2 => $ConditionsAlias,
408 #We only want things where the scrip applies to this sort of transaction
409 # TransactionBatch stage can define list of transaction
410 foreach( split /\s*,\s*/, ($args{'Type'} || '') ) {
412 ALIAS => $ConditionsAlias,
413 FIELD => 'ApplicableTransTypes',
416 ENTRYAGGREGATOR => 'OR',
420 # Or where the scrip applies to any transaction
422 ALIAS => $ConditionsAlias,
423 FIELD => 'ApplicableTransTypes',
426 ENTRYAGGREGATOR => 'OR',
429 # Promise some kind of ordering
430 $self->OrderBy( FIELD => 'Description' );
432 # we call Count below, but later we always do search
433 # so just do search and get count from results
434 $self->_DoSearch if $self->{'must_redo_search'};
437 "Found ". $self->Count ." scrips for $args{'Stage'} stage"
438 ." with applicable type(s) $args{'Type'}"
439 ." for txn #".$self->{TransactionObj}->Id
440 ." on ticket #".$self->{TicketObj}->Id