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 );
262 Loads scrip's condition and action modules.
269 $self->ConditionObj->LoadCondition;
270 $self->ActionObj->LoadAction;
273 # {{{ sub TemplateObj
277 Retuns an RT::Template object with this Scrip\'s Template
284 unless ( defined $self->{'TemplateObj'} ) {
285 require RT::Template;
286 $self->{'TemplateObj'} = RT::Template->new( $self->CurrentUser );
287 $self->{'TemplateObj'}->Load( $self->Template );
289 return ( $self->{'TemplateObj'} );
294 # {{{ Dealing with this instance of a scrip
298 =head2 Apply { TicketObj => undef, TransactionObj => undef}
300 This method instantiates the ScripCondition and ScripAction objects for a
301 single execution of this scrip. it then calls the IsApplicable method of the
303 If that succeeds, it calls the Prepare method of the
304 ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
306 Usually, the ticket and transaction objects passed to this method
307 should be loaded by the SuperUser role
312 # XXX TODO : This code appears to be obsoleted in favor of similar code in Scrips->Apply.
313 # Why is this here? Is it still called?
317 my %args = ( TicketObj => undef,
318 TransactionObj => undef,
321 $RT::Logger->debug("Now applying scrip ".$self->Id . " for transaction ".$args{'TransactionObj'}->id);
323 my $ApplicableTransactionObj = $self->IsApplicable( TicketObj => $args{'TicketObj'},
324 TransactionObj => $args{'TransactionObj'} );
325 unless ( $ApplicableTransactionObj ) {
329 if ( $ApplicableTransactionObj->id != $args{'TransactionObj'}->id ) {
330 $RT::Logger->debug("Found an applicable transaction ".$ApplicableTransactionObj->Id . " in the same batch with transaction ".$args{'TransactionObj'}->id);
333 #If it's applicable, prepare and commit it
334 $RT::Logger->debug("Now preparing scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
335 unless ( $self->Prepare( TicketObj => $args{'TicketObj'},
336 TransactionObj => $ApplicableTransactionObj )
341 $RT::Logger->debug("Now commiting scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
342 unless ( $self->Commit( TicketObj => $args{'TicketObj'},
343 TransactionObj => $ApplicableTransactionObj)
348 $RT::Logger->debug("We actually finished scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
355 # {{{ sub IsApplicable
359 Calls the Condition object\'s IsApplicable method
361 Upon success, returns the applicable Transaction object.
362 Otherwise, undef is returned.
364 If the Scrip is in the TransactionCreate Stage (the usual case), only test
365 the associated Transaction object to see if it is applicable.
367 For Scrips in the TransactionBatch Stage, test all Transaction objects
368 created during the Ticket object's lifetime, and returns the first one
375 my %args = ( TicketObj => undef,
376 TransactionObj => undef,
384 if ( $self->Stage eq 'TransactionCreate') {
385 # Only look at our current Transaction
386 @Transactions = ( $args{'TransactionObj'} );
388 elsif ( $self->Stage eq 'TransactionBatch') {
389 # Look at all Transactions in this Batch
390 @Transactions = @{ $args{'TicketObj'}->TransactionBatch || [] };
393 $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage );
396 my $ConditionObj = $self->ConditionObj;
397 foreach my $TransactionObj ( @Transactions ) {
398 # in TxnBatch stage we can select scrips that are not applicable to all txns
399 my $txn_type = $TransactionObj->Type;
400 next unless( $ConditionObj->ApplicableTransTypes =~ /(?:^|,)(?:Any|\Q$txn_type\E)(?:,|$)/i );
401 # Load the scrip's Condition object
402 $ConditionObj->LoadCondition(
404 TicketObj => $args{'TicketObj'},
405 TransactionObj => $TransactionObj,
408 if ( $ConditionObj->IsApplicable() ) {
409 # We found an application Transaction -- return it
410 $return = $TransactionObj;
417 $RT::Logger->error( "Scrip IsApplicable " . $self->Id . " died. - " . $@ );
431 Calls the action object's prepare method
437 my %args = ( TicketObj => undef,
438 TransactionObj => undef,
443 $self->ActionObj->LoadAction( ScripObj => $self,
444 TicketObj => $args{'TicketObj'},
445 TransactionObj => $args{'TransactionObj'},
448 $return = $self->ActionObj->Prepare();
451 $RT::Logger->error( "Scrip Prepare " . $self->Id . " died. - " . $@ );
465 Calls the action object's commit method
471 my %args = ( TicketObj => undef,
472 TransactionObj => undef,
477 $return = $self->ActionObj->Commit();
480 #Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
482 $args{'TicketObj'}->Load( $args{'TicketObj'}->Id );
485 $RT::Logger->error( "Scrip Commit " . $self->Id . " died. - " . $@ );
489 # Not destroying or weakening hte Action and Condition here could cause a
499 # {{{ ACL related methods
503 # does an acl check and then passes off the call
507 unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
509 "CurrentUser can't modify Scrips for " . $self->Queue . "\n" );
510 return ( 0, $self->loc('Permission Denied') );
512 return $self->__Set(@_);
518 # does an acl check and then passes off the call
522 unless ( $self->CurrentUserHasRight('ShowScrips') ) {
523 $RT::Logger->debug( "CurrentUser can't modify Scrips for "
524 . $self->__Value('Queue')
529 return $self->__Value(@_);
534 # {{{ sub CurrentUserHasRight
536 =head2 CurrentUserHasRight
538 Helper menthod for HasRight. Presets Principal to CurrentUser then
543 sub CurrentUserHasRight {
546 return ( $self->HasRight( Principal => $self->CurrentUser->UserObj,
557 Takes a param-hash consisting of "Right" and "Principal" Principal is
558 an RT::User object or an RT::CurrentUser object. "Right" is a textual
559 Right string that applies to Scrips.
565 my %args = ( Right => undef,
569 if ( $self->SUPER::_Value('Queue') ) {
570 return $args{'Principal'}->HasRight(
571 Right => $args{'Right'},
572 Object => $self->QueueObj
576 return $args{'Principal'}->HasRight(
577 Object => $RT::System,
578 Right => $args{'Right'},