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,
106 ConditionRules => undef,
107 ActionRules => undef,
111 unless ( $args{'Queue'} ) {
112 unless ( $self->CurrentUser->HasRight( Object => $RT::System,
113 Right => 'ModifyScrips' ) )
115 return ( 0, $self->loc('Permission Denied') );
117 $args{'Queue'} = 0; # avoid undef sneaking in
120 my $QueueObj = RT::Queue->new( $self->CurrentUser );
121 $QueueObj->Load( $args{'Queue'} );
122 unless ( $QueueObj->id ) {
123 return ( 0, $self->loc('Invalid queue') );
125 unless ( $QueueObj->CurrentUserHasRight('ModifyScrips') ) {
126 return ( 0, $self->loc('Permission Denied') );
128 $args{'Queue'} = $QueueObj->id;
131 #TODO +++ validate input
133 require RT::ScripAction;
134 return ( 0, $self->loc("Action is mandatory argument") )
135 unless $args{'ScripAction'};
136 my $action = RT::ScripAction->new( $self->CurrentUser );
137 $action->Load( $args{'ScripAction'} );
138 return ( 0, $self->loc( "Action '[_1]' not found", $args{'ScripAction'} ) )
141 require RT::Template;
142 return ( 0, $self->loc("Template is mandatory argument") )
143 unless $args{'Template'};
144 my $template = RT::Template->new( $self->CurrentUser );
145 $template->Load( $args{'Template'} );
146 return ( 0, $self->loc( "Template '[_1]' not found", $args{'Template'} ) )
147 unless $template->Id;
149 require RT::ScripCondition;
150 return ( 0, $self->loc("Condition is mandatory argument") )
151 unless $args{'ScripCondition'};
152 my $condition = RT::ScripCondition->new( $self->CurrentUser );
153 $condition->Load( $args{'ScripCondition'} );
154 return ( 0, $self->loc( "Condition '[_1]' not found", $args{'ScripCondition'} ) )
155 unless $condition->Id;
157 my ( $id, $msg ) = $self->SUPER::Create(
158 Queue => $args{'Queue'},
159 Template => $template->Id,
160 ScripCondition => $condition->id,
161 Stage => $args{'Stage'},
162 ScripAction => $action->Id,
163 Description => $args{'Description'},
164 CustomPrepareCode => $args{'CustomPrepareCode'},
165 CustomCommitCode => $args{'CustomCommitCode'},
166 CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
167 ConditionRules => $args{'ConditionRules'},
168 ActionRules => $args{'ActionRules'},
171 return ( $id, $self->loc('Scrip Created') );
174 return ( $id, $msg );
191 unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
192 return ( 0, $self->loc('Permission Denied') );
195 return ( $self->SUPER::Delete(@_) );
204 Retuns an RT::Queue object with this Scrip\'s queue
211 if ( !$self->{'QueueObj'} ) {
213 $self->{'QueueObj'} = RT::Queue->new( $self->CurrentUser );
214 $self->{'QueueObj'}->Load( $self->__Value('Queue') );
216 return ( $self->{'QueueObj'} );
225 Retuns an RT::Action object with this Scrip\'s Action
232 unless ( defined $self->{'ScripActionObj'} ) {
233 require RT::ScripAction;
235 $self->{'ScripActionObj'} = RT::ScripAction->new( $self->CurrentUser );
237 #TODO: why are we loading Actions with templates like this.
238 # two separate methods might make more sense
239 $self->{'ScripActionObj'}->Load( $self->ScripAction, $self->Template );
241 return ( $self->{'ScripActionObj'} );
246 # {{{ sub ConditionObj
250 Retuns an L<RT::ScripCondition> object with this Scrip's IsApplicable
257 my $res = RT::ScripCondition->new( $self->CurrentUser );
258 $res->Load( $self->ScripCondition );
266 Loads scrip's condition and action modules.
273 $self->ConditionObj->LoadCondition;
274 $self->ActionObj->LoadAction;
277 # {{{ sub TemplateObj
281 Retuns an RT::Template object with this Scrip\'s Template
288 unless ( defined $self->{'TemplateObj'} ) {
289 require RT::Template;
290 $self->{'TemplateObj'} = RT::Template->new( $self->CurrentUser );
291 $self->{'TemplateObj'}->Load( $self->Template );
293 return ( $self->{'TemplateObj'} );
298 # {{{ Dealing with this instance of a scrip
302 =head2 Apply { TicketObj => undef, TransactionObj => undef}
304 This method instantiates the ScripCondition and ScripAction objects for a
305 single execution of this scrip. it then calls the IsApplicable method of the
307 If that succeeds, it calls the Prepare method of the
308 ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
310 Usually, the ticket and transaction objects passed to this method
311 should be loaded by the SuperUser role
316 # XXX TODO : This code appears to be obsoleted in favor of similar code in Scrips->Apply.
317 # Why is this here? Is it still called?
321 my %args = ( TicketObj => undef,
322 TransactionObj => undef,
325 $RT::Logger->debug("Now applying scrip ".$self->Id . " for transaction ".$args{'TransactionObj'}->id);
327 my $ApplicableTransactionObj = $self->IsApplicable( TicketObj => $args{'TicketObj'},
328 TransactionObj => $args{'TransactionObj'} );
329 unless ( $ApplicableTransactionObj ) {
333 if ( $ApplicableTransactionObj->id != $args{'TransactionObj'}->id ) {
334 $RT::Logger->debug("Found an applicable transaction ".$ApplicableTransactionObj->Id . " in the same batch with transaction ".$args{'TransactionObj'}->id);
337 #If it's applicable, prepare and commit it
338 $RT::Logger->debug("Now preparing scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
339 unless ( $self->Prepare( TicketObj => $args{'TicketObj'},
340 TransactionObj => $ApplicableTransactionObj )
345 $RT::Logger->debug("Now commiting scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
346 unless ( $self->Commit( TicketObj => $args{'TicketObj'},
347 TransactionObj => $ApplicableTransactionObj)
352 $RT::Logger->debug("We actually finished scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
359 # {{{ sub IsApplicable
363 Calls the Condition object\'s IsApplicable method
365 Upon success, returns the applicable Transaction object.
366 Otherwise, undef is returned.
368 If the Scrip is in the TransactionCreate Stage (the usual case), only test
369 the associated Transaction object to see if it is applicable.
371 For Scrips in the TransactionBatch Stage, test all Transaction objects
372 created during the Ticket object's lifetime, and returns the first one
379 my %args = ( TicketObj => undef,
380 TransactionObj => undef,
388 if ( $self->Stage eq 'TransactionCreate') {
389 # Only look at our current Transaction
390 @Transactions = ( $args{'TransactionObj'} );
392 elsif ( $self->Stage eq 'TransactionBatch') {
393 # Look at all Transactions in this Batch
394 @Transactions = @{ $args{'TicketObj'}->TransactionBatch || [] };
397 $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage );
400 my $ConditionObj = $self->ConditionObj;
401 foreach my $TransactionObj ( @Transactions ) {
402 # in TxnBatch stage we can select scrips that are not applicable to all txns
403 my $txn_type = $TransactionObj->Type;
404 next unless( $ConditionObj->ApplicableTransTypes =~ /(?:^|,)(?:Any|\Q$txn_type\E)(?:,|$)/i );
405 # Load the scrip's Condition object
406 $ConditionObj->LoadCondition(
408 TicketObj => $args{'TicketObj'},
409 TransactionObj => $TransactionObj,
412 if ( $ConditionObj->IsApplicable() ) {
413 # We found an application Transaction -- return it
414 $return = $TransactionObj;
421 $RT::Logger->error( "Scrip IsApplicable " . $self->Id . " died. - " . $@ );
435 Calls the action object's prepare method
441 my %args = ( TicketObj => undef,
442 TransactionObj => undef,
447 $self->ActionObj->LoadAction( ScripObj => $self,
448 TicketObj => $args{'TicketObj'},
449 TransactionObj => $args{'TransactionObj'},
452 $return = $self->ActionObj->Prepare();
455 $RT::Logger->error( "Scrip Prepare " . $self->Id . " died. - " . $@ );
469 Calls the action object's commit method
475 my %args = ( TicketObj => undef,
476 TransactionObj => undef,
481 $return = $self->ActionObj->Commit();
484 #Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
486 $args{'TicketObj'}->Load( $args{'TicketObj'}->Id );
489 $RT::Logger->error( "Scrip Commit " . $self->Id . " died. - " . $@ );
493 # Not destroying or weakening hte Action and Condition here could cause a
503 # {{{ ACL related methods
507 # does an acl check and then passes off the call
511 unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
513 "CurrentUser can't modify Scrips for " . $self->Queue . "\n" );
514 return ( 0, $self->loc('Permission Denied') );
516 return $self->__Set(@_);
522 # does an acl check and then passes off the call
526 unless ( $self->CurrentUserHasRight('ShowScrips') ) {
527 $RT::Logger->debug( "CurrentUser can't modify Scrips for "
528 . $self->__Value('Queue')
533 return $self->__Value(@_);
538 # {{{ sub CurrentUserHasRight
540 =head2 CurrentUserHasRight
542 Helper menthod for HasRight. Presets Principal to CurrentUser then
547 sub CurrentUserHasRight {
550 return ( $self->HasRight( Principal => $self->CurrentUser->UserObj,
561 Takes a param-hash consisting of "Right" and "Principal" Principal is
562 an RT::User object or an RT::CurrentUser object. "Right" is a textual
563 Right string that applies to Scrips.
569 my %args = ( Right => undef,
573 if ( $self->SUPER::_Value('Queue') ) {
574 return $args{'Principal'}->HasRight(
575 Right => $args{'Right'},
576 Object => $self->QueueObj
580 return $args{'Principal'}->HasRight(
581 Object => $RT::System,
582 Right => $args{'Right'},