1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
6 # <jesse@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::Scrip - an RT Scrip object
69 no warnings qw(redefine);
75 Creates a new entry in the Scrips table. Takes a paramhash with:
81 ScripCondition => undef,
82 CustomPrepareCode => undef,
83 CustomCommitCode => undef,
84 CustomIsApplicableCode => undef,
89 Returns (retval, msg);
90 retval is 0 for failure or scrip id. msg is a textual description of what happened.
98 Template => 0, # name or id
99 ScripAction => 0, # name or id
100 ScripCondition => 0, # name or id
101 Stage => 'TransactionCreate',
102 Description => undef,
103 CustomPrepareCode => undef,
104 CustomCommitCode => undef,
105 CustomIsApplicableCode => undef,
109 unless ( $args{'Queue'} ) {
110 unless ( $self->CurrentUser->HasRight( Object => $RT::System,
111 Right => 'ModifyScrips' ) )
113 return ( 0, $self->loc('Permission Denied') );
115 $args{'Queue'} = 0; # avoid undef sneaking in
118 my $QueueObj = RT::Queue->new( $self->CurrentUser );
119 $QueueObj->Load( $args{'Queue'} );
120 unless ( $QueueObj->id ) {
121 return ( 0, $self->loc('Invalid queue') );
123 unless ( $QueueObj->CurrentUserHasRight('ModifyScrips') ) {
124 return ( 0, $self->loc('Permission Denied') );
126 $args{'Queue'} = $QueueObj->id;
129 #TODO +++ validate input
131 require RT::ScripAction;
132 return ( 0, $self->loc("Action is mandatory argument") )
133 unless $args{'ScripAction'};
134 my $action = RT::ScripAction->new( $self->CurrentUser );
135 $action->Load( $args{'ScripAction'} );
136 return ( 0, $self->loc( "Action '[_1]' not found", $args{'ScripAction'} ) )
139 require RT::Template;
140 return ( 0, $self->loc("Template is mandatory argument") )
141 unless $args{'Template'};
142 my $template = RT::Template->new( $self->CurrentUser );
143 $template->Load( $args{'Template'} );
144 return ( 0, $self->loc( "Template '[_1]' not found", $args{'Template'} ) )
145 unless $template->Id;
147 require RT::ScripCondition;
148 return ( 0, $self->loc("Condition is mandatory argument") )
149 unless $args{'ScripCondition'};
150 my $condition = RT::ScripCondition->new( $self->CurrentUser );
151 $condition->Load( $args{'ScripCondition'} );
152 return ( 0, $self->loc( "Condition '[_1]' not found", $args{'ScripCondition'} ) )
153 unless $condition->Id;
155 my ( $id, $msg ) = $self->SUPER::Create(
156 Queue => $args{'Queue'},
157 Template => $template->Id,
158 ScripCondition => $condition->id,
159 Stage => $args{'Stage'},
160 ScripAction => $action->Id,
161 Description => $args{'Description'},
162 CustomPrepareCode => $args{'CustomPrepareCode'},
163 CustomCommitCode => $args{'CustomCommitCode'},
164 CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
167 return ( $id, $self->loc('Scrip Created') );
170 return ( $id, $msg );
187 unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
188 return ( 0, $self->loc('Permission Denied') );
191 return ( $self->SUPER::Delete(@_) );
200 Retuns an RT::Queue object with this Scrip\'s queue
207 if ( !$self->{'QueueObj'} ) {
209 $self->{'QueueObj'} = RT::Queue->new( $self->CurrentUser );
210 $self->{'QueueObj'}->Load( $self->__Value('Queue') );
212 return ( $self->{'QueueObj'} );
221 Retuns an RT::Action object with this Scrip\'s Action
228 unless ( defined $self->{'ScripActionObj'} ) {
229 require RT::ScripAction;
231 $self->{'ScripActionObj'} = RT::ScripAction->new( $self->CurrentUser );
233 #TODO: why are we loading Actions with templates like this.
234 # two separate methods might make more sense
235 $self->{'ScripActionObj'}->Load( $self->ScripAction, $self->Template );
237 return ( $self->{'ScripActionObj'} );
242 # {{{ sub ConditionObj
246 Retuns an L<RT::ScripCondition> object with this Scrip's IsApplicable
253 my $res = RT::ScripCondition->new( $self->CurrentUser );
254 $res->Load( $self->ScripCondition );
260 # {{{ sub TemplateObj
264 Retuns an RT::Template object with this Scrip\'s Template
271 unless ( defined $self->{'TemplateObj'} ) {
272 require RT::Template;
273 $self->{'TemplateObj'} = RT::Template->new( $self->CurrentUser );
274 $self->{'TemplateObj'}->Load( $self->Template );
276 return ( $self->{'TemplateObj'} );
281 # {{{ Dealing with this instance of a scrip
285 =head2 Apply { TicketObj => undef, TransactionObj => undef}
287 This method instantiates the ScripCondition and ScripAction objects for a
288 single execution of this scrip. it then calls the IsApplicable method of the
290 If that succeeds, it calls the Prepare method of the
291 ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
293 Usually, the ticket and transaction objects passed to this method
294 should be loaded by the SuperUser role
299 # XXX TODO : This code appears to be obsoleted in favor of similar code in Scrips->Apply.
300 # Why is this here? Is it still called?
304 my %args = ( TicketObj => undef,
305 TransactionObj => undef,
308 $RT::Logger->debug("Now applying scrip ".$self->Id . " for transaction ".$args{'TransactionObj'}->id);
310 my $ApplicableTransactionObj = $self->IsApplicable( TicketObj => $args{'TicketObj'},
311 TransactionObj => $args{'TransactionObj'} );
312 unless ( $ApplicableTransactionObj ) {
316 if ( $ApplicableTransactionObj->id != $args{'TransactionObj'}->id ) {
317 $RT::Logger->debug("Found an applicable transaction ".$ApplicableTransactionObj->Id . " in the same batch with transaction ".$args{'TransactionObj'}->id);
320 #If it's applicable, prepare and commit it
321 $RT::Logger->debug("Now preparing scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
322 unless ( $self->Prepare( TicketObj => $args{'TicketObj'},
323 TransactionObj => $ApplicableTransactionObj )
328 $RT::Logger->debug("Now commiting scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
329 unless ( $self->Commit( TicketObj => $args{'TicketObj'},
330 TransactionObj => $ApplicableTransactionObj)
335 $RT::Logger->debug("We actually finished scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
342 # {{{ sub IsApplicable
346 Calls the Condition object\'s IsApplicable method
348 Upon success, returns the applicable Transaction object.
349 Otherwise, undef is returned.
351 If the Scrip is in the TransactionCreate Stage (the usual case), only test
352 the associated Transaction object to see if it is applicable.
354 For Scrips in the TransactionBatch Stage, test all Transaction objects
355 created during the Ticket object's lifetime, and returns the first one
362 my %args = ( TicketObj => undef,
363 TransactionObj => undef,
371 if ( $self->Stage eq 'TransactionCreate') {
372 # Only look at our current Transaction
373 @Transactions = ( $args{'TransactionObj'} );
375 elsif ( $self->Stage eq 'TransactionBatch') {
376 # Look at all Transactions in this Batch
377 @Transactions = @{ $args{'TicketObj'}->TransactionBatch || [] };
380 $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage );
383 my $ConditionObj = $self->ConditionObj;
384 foreach my $TransactionObj ( @Transactions ) {
385 # in TxnBatch stage we can select scrips that are not applicable to all txns
386 my $txn_type = $TransactionObj->Type;
387 next unless( $ConditionObj->ApplicableTransTypes =~ /(?:^|,)(?:Any|\Q$txn_type\E)(?:,|$)/i );
388 # Load the scrip's Condition object
389 $ConditionObj->LoadCondition(
391 TicketObj => $args{'TicketObj'},
392 TransactionObj => $TransactionObj,
395 if ( $ConditionObj->IsApplicable() ) {
396 # We found an application Transaction -- return it
397 $return = $TransactionObj;
404 $RT::Logger->error( "Scrip IsApplicable " . $self->Id . " died. - " . $@ );
418 Calls the action object's prepare method
424 my %args = ( TicketObj => undef,
425 TransactionObj => undef,
430 $self->ActionObj->LoadAction( ScripObj => $self,
431 TicketObj => $args{'TicketObj'},
432 TransactionObj => $args{'TransactionObj'},
435 $return = $self->ActionObj->Prepare();
438 $RT::Logger->error( "Scrip Prepare " . $self->Id . " died. - " . $@ );
452 Calls the action object's commit method
458 my %args = ( TicketObj => undef,
459 TransactionObj => undef,
464 $return = $self->ActionObj->Commit();
467 #Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
469 $args{'TicketObj'}->Load( $args{'TicketObj'}->Id );
472 $RT::Logger->error( "Scrip Commit " . $self->Id . " died. - " . $@ );
476 # Not destroying or weakening hte Action and Condition here could cause a
486 # {{{ ACL related methods
490 # does an acl check and then passes off the call
494 unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
496 "CurrentUser can't modify Scrips for " . $self->Queue . "\n" );
497 return ( 0, $self->loc('Permission Denied') );
499 return $self->__Set(@_);
505 # does an acl check and then passes off the call
509 unless ( $self->CurrentUserHasRight('ShowScrips') ) {
510 $RT::Logger->debug( "CurrentUser can't modify Scrips for "
511 . $self->__Value('Queue')
516 return $self->__Value(@_);
521 # {{{ sub CurrentUserHasRight
523 =head2 CurrentUserHasRight
525 Helper menthod for HasRight. Presets Principal to CurrentUser then
530 sub CurrentUserHasRight {
533 return ( $self->HasRight( Principal => $self->CurrentUser->UserObj,
544 Takes a param-hash consisting of "Right" and "Principal" Principal is
545 an RT::User object or an RT::CurrentUser object. "Right" is a textual
546 Right string that applies to Scrips.
552 my %args = ( Right => undef,
556 if ( $self->SUPER::_Value('Queue') ) {
557 return $args{'Principal'}->HasRight(
558 Right => $args{'Right'},
559 Object => $self->QueueObj
563 return $args{'Principal'}->HasRight(
564 Object => $RT::System,
565 Right => $args{'Right'},