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::Scrip - an RT Scrip object
74 use RT::ScripCondition;
76 use base 'RT::Record';
84 Creates a new entry in the Scrips table. Takes a paramhash with:
90 ScripCondition => undef,
91 CustomPrepareCode => undef,
92 CustomCommitCode => undef,
93 CustomIsApplicableCode => undef,
98 Returns (retval, msg);
99 retval is 0 for failure or scrip id. msg is a textual description of what happened.
107 Template => 0, # name or id
108 ScripAction => 0, # name or id
109 ScripCondition => 0, # name or id
110 Stage => 'TransactionCreate',
111 Description => undef,
112 CustomPrepareCode => undef,
113 CustomCommitCode => undef,
114 CustomIsApplicableCode => undef,
115 ConditionRules => undef,
116 ActionRules => undef,
120 if ($args{CustomPrepareCode} || $args{CustomCommitCode} || $args{CustomIsApplicableCode}) {
121 unless ( $self->CurrentUser->HasRight( Object => $RT::System,
122 Right => 'ExecuteCode' ) )
124 return ( 0, $self->loc('Permission Denied') );
128 unless ( $args{'Queue'} ) {
129 unless ( $self->CurrentUser->HasRight( Object => $RT::System,
130 Right => 'ModifyScrips' ) )
132 return ( 0, $self->loc('Permission Denied') );
134 $args{'Queue'} = 0; # avoid undef sneaking in
137 my $QueueObj = RT::Queue->new( $self->CurrentUser );
138 $QueueObj->Load( $args{'Queue'} );
139 unless ( $QueueObj->id ) {
140 return ( 0, $self->loc('Invalid queue') );
142 unless ( $QueueObj->CurrentUserHasRight('ModifyScrips') ) {
143 return ( 0, $self->loc('Permission Denied') );
145 $args{'Queue'} = $QueueObj->id;
148 #TODO +++ validate input
150 require RT::ScripAction;
151 return ( 0, $self->loc("Action is mandatory argument") )
152 unless $args{'ScripAction'};
153 my $action = RT::ScripAction->new( $self->CurrentUser );
154 $action->Load( $args{'ScripAction'} );
155 return ( 0, $self->loc( "Action '[_1]' not found", $args{'ScripAction'} ) )
158 require RT::Template;
159 return ( 0, $self->loc("Template is mandatory argument") )
160 unless $args{'Template'};
161 my $template = RT::Template->new( $self->CurrentUser );
162 $template->Load( $args{'Template'} );
163 return ( 0, $self->loc( "Template '[_1]' not found", $args{'Template'} ) )
164 unless $template->Id;
166 require RT::ScripCondition;
167 return ( 0, $self->loc("Condition is mandatory argument") )
168 unless $args{'ScripCondition'};
169 my $condition = RT::ScripCondition->new( $self->CurrentUser );
170 $condition->Load( $args{'ScripCondition'} );
171 return ( 0, $self->loc( "Condition '[_1]' not found", $args{'ScripCondition'} ) )
172 unless $condition->Id;
174 my ( $id, $msg ) = $self->SUPER::Create(
175 Queue => $args{'Queue'},
176 Template => $template->Id,
177 ScripCondition => $condition->id,
178 Stage => $args{'Stage'},
179 ScripAction => $action->Id,
180 Description => $args{'Description'},
181 CustomPrepareCode => $args{'CustomPrepareCode'},
182 CustomCommitCode => $args{'CustomCommitCode'},
183 CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
184 ConditionRules => $args{'ConditionRules'},
185 ActionRules => $args{'ActionRules'},
188 return ( $id, $self->loc('Scrip Created') );
191 return ( $id, $msg );
206 unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
207 return ( 0, $self->loc('Permission Denied') );
210 return ( $self->SUPER::Delete(@_) );
217 Retuns an RT::Queue object with this Scrip's queue
224 if ( !$self->{'QueueObj'} ) {
226 $self->{'QueueObj'} = RT::Queue->new( $self->CurrentUser );
227 $self->{'QueueObj'}->Load( $self->__Value('Queue') );
229 return ( $self->{'QueueObj'} );
236 Retuns an RT::Action object with this Scrip\'s Action
243 unless ( defined $self->{'ScripActionObj'} ) {
244 require RT::ScripAction;
246 $self->{'ScripActionObj'} = RT::ScripAction->new( $self->CurrentUser );
248 #TODO: why are we loading Actions with templates like this.
249 # two separate methods might make more sense
250 $self->{'ScripActionObj'}->Load( $self->ScripAction, $self->Template );
252 return ( $self->{'ScripActionObj'} );
259 Retuns an L<RT::ScripCondition> object with this Scrip's IsApplicable
266 my $res = RT::ScripCondition->new( $self->CurrentUser );
267 $res->Load( $self->ScripCondition );
274 Loads scrip's condition and action modules.
281 $self->ConditionObj->LoadCondition;
282 $self->ActionObj->LoadAction;
288 Retuns an RT::Template object with this Scrip\'s Template
295 unless ( defined $self->{'TemplateObj'} ) {
296 require RT::Template;
297 $self->{'TemplateObj'} = RT::Template->new( $self->CurrentUser );
298 $self->{'TemplateObj'}->Load( $self->Template );
300 return ( $self->{'TemplateObj'} );
306 =head2 Apply { TicketObj => undef, TransactionObj => undef}
308 This method instantiates the ScripCondition and ScripAction objects for a
309 single execution of this scrip. it then calls the IsApplicable method of the
311 If that succeeds, it calls the Prepare method of the
312 ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
314 Usually, the ticket and transaction objects passed to this method
315 should be loaded by the SuperUser role
320 # XXX TODO : This code appears to be obsoleted in favor of similar code in Scrips->Apply.
321 # Why is this here? Is it still called?
325 my %args = ( TicketObj => undef,
326 TransactionObj => undef,
329 $RT::Logger->debug("Now applying scrip ".$self->Id . " for transaction ".$args{'TransactionObj'}->id);
331 my $ApplicableTransactionObj = $self->IsApplicable( TicketObj => $args{'TicketObj'},
332 TransactionObj => $args{'TransactionObj'} );
333 unless ( $ApplicableTransactionObj ) {
337 if ( $ApplicableTransactionObj->id != $args{'TransactionObj'}->id ) {
338 $RT::Logger->debug("Found an applicable transaction ".$ApplicableTransactionObj->Id . " in the same batch with transaction ".$args{'TransactionObj'}->id);
341 #If it's applicable, prepare and commit it
342 $RT::Logger->debug("Now preparing scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
343 unless ( $self->Prepare( TicketObj => $args{'TicketObj'},
344 TransactionObj => $ApplicableTransactionObj )
349 $RT::Logger->debug("Now commiting scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
350 unless ( $self->Commit( TicketObj => $args{'TicketObj'},
351 TransactionObj => $ApplicableTransactionObj)
356 $RT::Logger->debug("We actually finished scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
365 Calls the Condition object\'s IsApplicable method
367 Upon success, returns the applicable Transaction object.
368 Otherwise, undef is returned.
370 If the Scrip is in the TransactionCreate Stage (the usual case), only test
371 the associated Transaction object to see if it is applicable.
373 For Scrips in the TransactionBatch Stage, test all Transaction objects
374 created during the Ticket object's lifetime, and returns the first one
381 my %args = ( TicketObj => undef,
382 TransactionObj => undef,
390 if ( $self->Stage eq 'TransactionCreate') {
391 # Only look at our current Transaction
392 @Transactions = ( $args{'TransactionObj'} );
394 elsif ( $self->Stage eq 'TransactionBatch') {
395 # Look at all Transactions in this Batch
396 @Transactions = @{ $args{'TicketObj'}->TransactionBatch || [] };
399 $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage );
402 my $ConditionObj = $self->ConditionObj;
403 foreach my $TransactionObj ( @Transactions ) {
404 # in TxnBatch stage we can select scrips that are not applicable to all txns
405 my $txn_type = $TransactionObj->Type;
406 next unless( $ConditionObj->ApplicableTransTypes =~ /(?:^|,)(?:Any|\Q$txn_type\E)(?:,|$)/i );
407 # Load the scrip's Condition object
408 $ConditionObj->LoadCondition(
410 TicketObj => $args{'TicketObj'},
411 TransactionObj => $TransactionObj,
414 if ( $ConditionObj->IsApplicable() ) {
415 # We found an application Transaction -- return it
416 $return = $TransactionObj;
423 $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. - " . $@ );
467 Calls the action object's commit method
473 my %args = ( TicketObj => undef,
474 TransactionObj => undef,
479 $return = $self->ActionObj->Commit();
482 #Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
484 $args{'TicketObj'}->Load( $args{'TicketObj'}->Id );
487 $RT::Logger->error( "Scrip Commit " . $self->Id . " died. - " . $@ );
491 # Not destroying or weakening hte Action and Condition here could cause a
501 # does an acl check and then passes off the call
510 unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
512 "CurrentUser can't modify Scrips for " . $self->Queue . "\n" );
513 return ( 0, $self->loc('Permission Denied') );
517 if (length($args{Value})) {
518 if ($args{Field} eq 'CustomIsApplicableCode' || $args{Field} eq 'CustomPrepareCode' || $args{Field} eq 'CustomCommitCode') {
519 unless ( $self->CurrentUser->HasRight( Object => $RT::System,
520 Right => 'ExecuteCode' ) ) {
521 return ( 0, $self->loc('Permission Denied') );
526 return $self->__Set(@_);
530 # does an acl check and then passes off the call
534 unless ( $self->CurrentUserHasRight('ShowScrips') ) {
535 $RT::Logger->debug( "CurrentUser can't modify Scrips for "
536 . $self->__Value('Queue')
541 return $self->__Value(@_);
546 =head2 CurrentUserHasRight
548 Helper menthod for HasRight. Presets Principal to CurrentUser then
553 sub CurrentUserHasRight {
556 return ( $self->HasRight( Principal => $self->CurrentUser->UserObj,
565 Takes a param-hash consisting of "Right" and "Principal" Principal is
566 an RT::User object or an RT::CurrentUser object. "Right" is a textual
567 Right string that applies to Scrips.
573 my %args = ( Right => undef,
577 if ( $self->SUPER::_Value('Queue') ) {
578 return $args{'Principal'}->HasRight(
579 Right => $args{'Right'},
580 Object => $self->QueueObj
584 return $args{'Principal'}->HasRight(
585 Object => $RT::System,
586 Right => $args{'Right'},
595 This routine compile-checks the custom prepare, commit, and is-applicable code
596 to see if they are syntactically valid Perl. We eval them in a codeblock to
597 avoid actually executing the code.
599 If one of the fields has a compile error, only the first is reported.
601 Returns an (ok, message) pair.
608 for my $method (qw/CustomPrepareCode CustomCommitCode CustomIsApplicableCode/) {
609 my $code = $self->$method;
610 next if !defined($code);
614 eval "sub { $code }";
619 return (0, $self->loc("Couldn't compile [_1] codeblock '[_2]': [_3]", $method, $code, $error));
624 =head2 SetScripAction
632 return ( 0, $self->loc("Action is mandatory argument") ) unless $value;
634 require RT::ScripAction;
635 my $action = RT::ScripAction->new( $self->CurrentUser );
636 $action->Load($value);
637 return ( 0, $self->loc( "Action '[_1]' not found", $value ) )
640 return $self->_Set( Field => 'ScripAction', Value => $action->Id );
643 =head2 SetScripCondition
647 sub SetScripCondition {
651 return ( 0, $self->loc("Condition is mandatory argument") )
654 require RT::ScripCondition;
655 my $condition = RT::ScripCondition->new( $self->CurrentUser );
656 $condition->Load($value);
658 return ( 0, $self->loc( "Condition '[_1]' not found", $value ) )
659 unless $condition->Id;
661 return $self->_Set( Field => 'ScripCondition', Value => $condition->Id );
672 return ( 0, $self->loc("Template is mandatory argument") ) unless $value;
674 require RT::Template;
675 my $template = RT::Template->new( $self->CurrentUser );
676 $template->Load($value);
677 return ( 0, $self->loc( "Template '[_1]' not found", $value ) )
678 unless $template->Id;
680 return $self->_Set( Field => 'Template', Value => $template->Id );
692 Returns the current value of id.
693 (In the database, id is stored as int(11).)
701 Returns the current value of Description.
702 (In the database, Description is stored as varchar(255).)
706 =head2 SetDescription VALUE
709 Set Description to VALUE.
710 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
711 (In the database, Description will be stored as a varchar(255).)
717 =head2 ScripCondition
719 Returns the current value of ScripCondition.
720 (In the database, ScripCondition is stored as int(11).)
724 =head2 SetScripCondition VALUE
727 Set ScripCondition to VALUE.
728 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
729 (In the database, ScripCondition will be stored as a int(11).)
735 =head2 ScripConditionObj
737 Returns the ScripCondition Object which has the id returned by ScripCondition
742 sub ScripConditionObj {
744 my $ScripCondition = RT::ScripCondition->new($self->CurrentUser);
745 $ScripCondition->Load($self->__Value('ScripCondition'));
746 return($ScripCondition);
751 Returns the current value of ScripAction.
752 (In the database, ScripAction is stored as int(11).)
756 =head2 SetScripAction VALUE
759 Set ScripAction to VALUE.
760 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
761 (In the database, ScripAction will be stored as a int(11).)
767 =head2 ScripActionObj
769 Returns the ScripAction Object which has the id returned by ScripAction
776 my $ScripAction = RT::ScripAction->new($self->CurrentUser);
777 $ScripAction->Load($self->__Value('ScripAction'));
778 return($ScripAction);
781 =head2 ConditionRules
783 Returns the current value of ConditionRules.
784 (In the database, ConditionRules is stored as text.)
788 =head2 SetConditionRules VALUE
791 Set ConditionRules to VALUE.
792 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
793 (In the database, ConditionRules will be stored as a text.)
801 Returns the current value of ActionRules.
802 (In the database, ActionRules is stored as text.)
806 =head2 SetActionRules VALUE
809 Set ActionRules to VALUE.
810 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
811 (In the database, ActionRules will be stored as a text.)
817 =head2 CustomIsApplicableCode
819 Returns the current value of CustomIsApplicableCode.
820 (In the database, CustomIsApplicableCode is stored as text.)
824 =head2 SetCustomIsApplicableCode VALUE
827 Set CustomIsApplicableCode to VALUE.
828 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
829 (In the database, CustomIsApplicableCode will be stored as a text.)
835 =head2 CustomPrepareCode
837 Returns the current value of CustomPrepareCode.
838 (In the database, CustomPrepareCode is stored as text.)
842 =head2 SetCustomPrepareCode VALUE
845 Set CustomPrepareCode to VALUE.
846 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
847 (In the database, CustomPrepareCode will be stored as a text.)
853 =head2 CustomCommitCode
855 Returns the current value of CustomCommitCode.
856 (In the database, CustomCommitCode is stored as text.)
860 =head2 SetCustomCommitCode VALUE
863 Set CustomCommitCode to VALUE.
864 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
865 (In the database, CustomCommitCode will be stored as a text.)
873 Returns the current value of Stage.
874 (In the database, Stage is stored as varchar(32).)
878 =head2 SetStage VALUE
882 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
883 (In the database, Stage will be stored as a varchar(32).)
891 Returns the current value of Queue.
892 (In the database, Queue is stored as int(11).)
896 =head2 SetQueue VALUE
900 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
901 (In the database, Queue will be stored as a int(11).)
909 Returns the current value of Template.
910 (In the database, Template is stored as int(11).)
914 =head2 SetTemplate VALUE
917 Set Template to VALUE.
918 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
919 (In the database, Template will be stored as a int(11).)
927 Returns the current value of Creator.
928 (In the database, Creator is stored as int(11).)
936 Returns the current value of Created.
937 (In the database, Created is stored as datetime.)
945 Returns the current value of LastUpdatedBy.
946 (In the database, LastUpdatedBy is stored as int(11).)
954 Returns the current value of LastUpdated.
955 (In the database, LastUpdated is stored as datetime.)
962 sub _CoreAccessible {
966 {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
968 {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
970 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
972 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
974 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
976 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
977 CustomIsApplicableCode =>
978 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
980 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
982 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
984 {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => ''},
986 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
988 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
990 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
992 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
994 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
996 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
1001 RT::Base->_ImportOverlays();