1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2012 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
74 use base 'RT::SearchBuilder';
81 Takes a queue id (numerical) as its only argument. Makes sure that
82 Scopes it pulls out apply to this queue (or another that you've selected with
83 another call to this method
91 $self->Limit (ENTRYAGGREGATOR => 'OR',
102 Scopes it pulls out apply to all queues (or another that you've selected with
103 another call to this method or LimitToQueue
111 $self->Limit (ENTRYAGGREGATOR => 'OR',
121 Returns the next scrip that this user can see.
129 my $Scrip = $self->SUPER::Next();
130 if ((defined($Scrip)) and (ref($Scrip))) {
132 if ($Scrip->CurrentUserHasRight('ShowScrips')) {
136 #If the user doesn't have the right to show this scrip
138 return($self->Next());
141 #if there never was any scrip
150 Run through the relevant scrips. Scrips will run in order based on
151 description. (Most common use case is to prepend a number to the description,
152 forcing the scrips to run in ascending alphanumerical order.)
159 my %args = ( TicketObj => undef,
161 Transaction => undef,
162 TransactionObj => undef,
167 $self->Prepare(%args);
174 Commit all of this object's prepared scrips
181 # RT::Scrips->_SetupSourceObjects will clobber
182 # the CurrentUser, but we need to keep this ticket
183 # so that the _TransactionBatch cache is maintained
184 # and doesn't run twice. sigh.
185 $self->_StashCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj};
187 #We're really going to need a non-acled ticket for the scrips to work
188 $self->_SetupSourceObjects( TicketObj => $self->{'TicketObj'},
189 TransactionObj => $self->{'TransactionObj'} );
191 foreach my $scrip (@{$self->Prepared}) {
193 "Committing scrip #". $scrip->id
194 ." on txn #". $self->{'TransactionObj'}->id
195 ." of ticket #". $self->{'TicketObj'}->id
198 $scrip->Commit( TicketObj => $self->{'TicketObj'},
199 TransactionObj => $self->{'TransactionObj'} );
203 $self->_RestoreCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj};
209 Only prepare the scrips, returning an array of the scrips we're interested in
210 in order of preparation, not execution
216 my %args = ( TicketObj => undef,
218 Transaction => undef,
219 TransactionObj => undef,
224 # RT::Scrips->_SetupSourceObjects will clobber
225 # the CurrentUser, but we need to keep this ticket
226 # so that the _TransactionBatch cache is maintained
227 # and doesn't run twice. sigh.
228 $self->_StashCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj};
230 #We're really going to need a non-acled ticket for the scrips to work
231 $self->_SetupSourceObjects( TicketObj => $args{'TicketObj'},
232 Ticket => $args{'Ticket'},
233 TransactionObj => $args{'TransactionObj'},
234 Transaction => $args{'Transaction'} );
237 $self->_FindScrips( Stage => $args{'Stage'}, Type => $args{'Type'} );
240 #Iterate through each script and check it's applicability.
241 while ( my $scrip = $self->Next() ) {
243 unless ( $scrip->IsApplicable(
244 TicketObj => $self->{'TicketObj'},
245 TransactionObj => $self->{'TransactionObj'}
247 $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it isn't applicable");
251 #If it's applicable, prepare and commit it
252 unless ( $scrip->Prepare( TicketObj => $self->{'TicketObj'},
253 TransactionObj => $self->{'TransactionObj'}
255 $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it didn't Prepare");
258 push @{$self->{'prepared_scrips'}}, $scrip;
263 $self->_RestoreCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj};
266 return (@{$self->Prepared});
272 Returns an arrayref of the scrips this object has prepared
279 return ($self->{'prepared_scrips'} || []);
282 =head2 _StashCurrentUser TicketObj => RT::Ticket
284 Saves aside the current user of the original ticket that was passed to these scrips.
285 This is used to make sure that we don't accidentally leak the RT_System current user
286 back to the calling code.
290 sub _StashCurrentUser {
294 $self->{_TicketCurrentUser} = $args{TicketObj}->CurrentUser;
297 =head2 _RestoreCurrentUser TicketObj => RT::Ticket
299 Uses the current user saved by _StashCurrentUser to reset a Ticket object
300 back to the caller's current user and avoid leaking an RT_System ticket to
305 sub _RestoreCurrentUser {
308 unless ( $self->{_TicketCurrentUser} ) {
309 RT->Logger->debug("Called _RestoreCurrentUser without a stashed current user object");
312 $args{TicketObj}->CurrentUser($self->{_TicketCurrentUser});
316 =head2 _SetupSourceObjects { TicketObj , Ticket, Transaction, TransactionObj }
318 Setup a ticket and transaction for this Scrip collection to work with as it runs through the
319 relevant scrips. (Also to figure out which scrips apply)
326 sub _SetupSourceObjects {
332 Transaction => undef,
333 TransactionObj => undef,
337 if ( $self->{'TicketObj'} = $args{'TicketObj'} ) {
338 # This clobbers the passed in TicketObj by turning it into one
339 # whose current user is RT_System. Anywhere in the Web UI
340 # currently calling into this is thus susceptable to a privilege
341 # leak; the only current call site is ->Apply, which bandaids
342 # over the top of this by re-asserting the CurrentUser
344 $self->{'TicketObj'}->CurrentUser( $self->CurrentUser );
347 $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser );
348 $self->{'TicketObj'}->Load( $args{'Ticket'} )
349 || $RT::Logger->err("$self couldn't load ticket $args{'Ticket'}");
352 if ( ( $self->{'TransactionObj'} = $args{'TransactionObj'} ) ) {
353 $self->{'TransactionObj'}->CurrentUser( $self->CurrentUser );
356 $self->{'TransactionObj'} = RT::Transaction->new( $self->CurrentUser );
357 $self->{'TransactionObj'}->Load( $args{'Transaction'} )
358 || $RT::Logger->err( "$self couldn't load transaction $args{'Transaction'}");
366 Find only the apropriate scrips for whatever we're doing now. Order them
367 by their description. (Most common use case is to prepend a number to the
368 description, forcing the scrips to display and run in ascending alphanumerical
381 $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id )
382 ; #Limit it to $Ticket->QueueObj->Id
383 $self->LimitToGlobal();
386 $self->Limit( FIELD => "Stage", VALUE => $args{'Stage'} );
388 my $ConditionsAlias = $self->NewAlias('ScripConditions');
392 FIELD1 => 'ScripCondition',
393 ALIAS2 => $ConditionsAlias,
397 #We only want things where the scrip applies to this sort of transaction
398 # TransactionBatch stage can define list of transaction
399 foreach( split /\s*,\s*/, ($args{'Type'} || '') ) {
401 ALIAS => $ConditionsAlias,
402 FIELD => 'ApplicableTransTypes',
405 ENTRYAGGREGATOR => 'OR',
409 # Or where the scrip applies to any transaction
411 ALIAS => $ConditionsAlias,
412 FIELD => 'ApplicableTransTypes',
415 ENTRYAGGREGATOR => 'OR',
418 # Promise some kind of ordering
419 $self->OrderBy( FIELD => 'Description' );
421 # we call Count below, but later we always do search
422 # so just do search and get count from results
423 $self->_DoSearch if $self->{'must_redo_search'};
426 "Found ". $self->Count ." scrips for $args{'Stage'} stage"
427 ." with applicable type(s) $args{'Type'}"
428 ." for txn #".$self->{TransactionObj}->Id
429 ." on ticket #".$self->{TicketObj}->Id
438 Returns an empty new RT::Scrip item
444 return(RT::Scrip->new($self->CurrentUser));
446 RT::Base->_ImportOverlays();