summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Scrip_Overlay.pm
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/Scrip_Overlay.pm')
-rw-r--r--rt/lib/RT/Scrip_Overlay.pm616
1 files changed, 616 insertions, 0 deletions
diff --git a/rt/lib/RT/Scrip_Overlay.pm b/rt/lib/RT/Scrip_Overlay.pm
new file mode 100644
index 000000000..ae2782a23
--- /dev/null
+++ b/rt/lib/RT/Scrip_Overlay.pm
@@ -0,0 +1,616 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+=head1 NAME
+
+ RT::Scrip - an RT Scrip object
+
+=head1 SYNOPSIS
+
+ use RT::Scrip;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok (require RT::Scrip);
+
+
+my $q = RT::Queue->new($RT::SystemUser);
+$q->Create(Name => 'ScripTest');
+ok($q->Id, "Created a scriptest queue");
+
+my $s1 = RT::Scrip->new($RT::SystemUser);
+my ($val, $msg) =$s1->Create( Queue => $q->Id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ CustomIsApplicableCode => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}',
+ CustomPrepareCode => 'return 1',
+ CustomCommitCode => '$self->TicketObj->SetPriority("87");',
+ Template => 'Blank'
+ );
+ok($val,$msg);
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id,
+ Subject => "hair on fire",
+ );
+ok($tv, $tm);
+
+ok ($ticket->Priority == '87', "Ticket priority is set right");
+
+
+my $ticket2 = RT::Ticket->new($RT::SystemUser);
+my ($t2v,$t2tv,$t2m) = $ticket2->Create(Queue => $q->Id,
+ Subject => "hair in water",
+ );
+ok($t2v, $t2m);
+
+ok ($ticket2->Priority != '87', "Ticket priority is set right");
+
+
+=end testing
+
+=cut
+
+
+package RT::Scrip;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub Create
+
+=head2 Create
+
+Creates a new entry in the Scrips table. Takes a paramhash with:
+
+ Queue => 0,
+ Description => undef,
+ Template => undef,
+ ScripAction => undef,
+ ScripCondition => undef,
+ CustomPrepareCode => undef,
+ CustomCommitCode => undef,
+ CustomIsApplicableCode => undef,
+
+
+
+
+Returns (retval, msg);
+retval is 0 for failure or scrip id. msg is a textual description of what happened.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Queue => 0,
+ Template => 0, # name or id
+ ScripAction => 0, # name or id
+ ScripCondition => 0, # name or id
+ Stage => 'TransactionCreate',
+ Description => undef,
+ CustomPrepareCode => undef,
+ CustomCommitCode => undef,
+ CustomIsApplicableCode => undef,
+
+ @_ );
+
+ if ( !$args{'Queue'} ) {
+ unless ( $self->CurrentUser->HasRight( Object => $RT::System,
+ Right => 'ModifyScrips' )
+ ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $args{'Queue'} = 0; # avoid undef sneaking in
+ }
+ else {
+ my $QueueObj = new RT::Queue( $self->CurrentUser );
+ $QueueObj->Load( $args{'Queue'} );
+ unless ( $QueueObj->id() ) {
+ return ( 0, $self->loc('Invalid queue') );
+ }
+ unless ( $QueueObj->CurrentUserHasRight('ModifyScrips') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $args{'Queue'} = $QueueObj->id();
+ }
+
+ #TODO +++ validate input
+
+ require RT::ScripAction;
+ my $action = new RT::ScripAction( $self->CurrentUser );
+ if ( $args{'ScripAction'} ) {
+ $action->Load( $args{'ScripAction'} );
+ }
+ return ( 0, $self->loc( "Action [_1] not found", $args{'ScripAction'} ) )
+ unless $action->Id;
+
+ require RT::Template;
+ my $template = new RT::Template( $self->CurrentUser );
+ if ( $args{'Template'} ) {
+ $template->Load( $args{'Template'} );
+ }
+ return ( 0, $self->loc('Template not found') ) unless $template->Id;
+
+ require RT::ScripCondition;
+ my $condition = new RT::ScripCondition( $self->CurrentUser );
+ if ( $args{'ScripCondition'} ) {
+ $condition->Load( $args{'ScripCondition'} );
+ }
+ unless ( $condition->Id ) {
+ return ( 0, $self->loc('Condition not found') );
+ }
+
+ my ( $id, $msg ) = $self->SUPER::Create(
+ Queue => $args{'Queue'},
+ Template => $template->Id,
+ ScripCondition => $condition->id,
+ Stage => $args{'Stage'},
+ ScripAction => $action->Id,
+ Description => $args{'Description'},
+ CustomPrepareCode => $args{'CustomPrepareCode'},
+ CustomCommitCode => $args{'CustomCommitCode'},
+ CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
+
+ );
+ if ($id) {
+ return ( $id, $self->loc('Scrip Created') );
+ }
+ else {
+ return ( $id, $msg );
+ }
+}
+
+# }}}
+
+# {{{ sub Delete
+
+=head2 Delete
+
+Delete this object
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ return ( $self->SUPER::Delete(@_) );
+}
+
+# }}}
+
+# {{{ sub QueueObj
+
+=head2 QueueObj
+
+Retuns an RT::Queue object with this Scrip\'s queue
+
+=cut
+
+sub QueueObj {
+ my $self = shift;
+
+ if ( !$self->{'QueueObj'} ) {
+ require RT::Queue;
+ $self->{'QueueObj'} = RT::Queue->new( $self->CurrentUser );
+ $self->{'QueueObj'}->Load( $self->__Value('Queue') );
+ }
+ return ( $self->{'QueueObj'} );
+}
+
+# }}}
+
+# {{{ sub ActionObj
+
+=head2 ActionObj
+
+Retuns an RT::Action object with this Scrip\'s Action
+
+=cut
+
+sub ActionObj {
+ my $self = shift;
+
+ unless ( defined $self->{'ScripActionObj'} ) {
+ require RT::ScripAction;
+
+ $self->{'ScripActionObj'} = RT::ScripAction->new( $self->CurrentUser );
+
+ #TODO: why are we loading Actions with templates like this.
+ # two separate methods might make more sense
+ $self->{'ScripActionObj'}->Load( $self->ScripAction, $self->Template );
+ }
+ return ( $self->{'ScripActionObj'} );
+}
+
+# }}}
+
+# {{{ sub ConditionObj
+
+=head2 ConditionObj
+
+Retuns an RT::ScripCondition object with this Scrip's IsApplicable
+
+=cut
+
+sub ConditionObj {
+ my $self = shift;
+
+ unless ( defined $self->{'ScripConditionObj'} ) {
+ require RT::ScripCondition;
+ $self->{'ScripConditionObj'} =
+ RT::ScripCondition->new( $self->CurrentUser );
+ if ( $self->ScripCondition ) {
+ $self->{'ScripConditionObj'}->Load( $self->ScripCondition );
+ }
+ }
+ return ( $self->{'ScripConditionObj'} );
+}
+
+# }}}
+
+# {{{ sub TemplateObj
+
+=head2 TemplateObj
+
+Retuns an RT::Template object with this Scrip\'s Template
+
+=cut
+
+sub TemplateObj {
+ my $self = shift;
+
+ unless ( defined $self->{'TemplateObj'} ) {
+ require RT::Template;
+ $self->{'TemplateObj'} = RT::Template->new( $self->CurrentUser );
+ $self->{'TemplateObj'}->Load( $self->Template );
+ }
+ return ( $self->{'TemplateObj'} );
+}
+
+# }}}
+
+# {{{ Dealing with this instance of a scrip
+
+# {{{ sub Apply
+
+=head2 Apply { TicketObj => undef, TransactionObj => undef}
+
+This method instantiates the ScripCondition and ScripAction objects for a
+single execution of this scrip. it then calls the IsApplicable method of the
+ScripCondition.
+If that succeeds, it calls the Prepare method of the
+ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
+
+Usually, the ticket and transaction objects passed to this method
+should be loaded by the SuperUser role
+
+=cut
+
+
+# XXX TODO : This code appears to be obsoleted in favor of similar code in Scrips->Apply.
+# Why is this here? Is it still called?
+
+sub Apply {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ $RT::Logger->debug("Now applying scrip ".$self->Id . " for transaction ".$args{'TransactionObj'}->id);
+
+ my $ApplicableTransactionObj = $self->IsApplicable( TicketObj => $args{'TicketObj'},
+ TransactionObj => $args{'TransactionObj'} );
+ unless ( $ApplicableTransactionObj ) {
+ return undef;
+ }
+
+ if ( $ApplicableTransactionObj->id != $args{'TransactionObj'}->id ) {
+ $RT::Logger->debug("Found an applicable transaction ".$ApplicableTransactionObj->Id . " in the same batch with transaction ".$args{'TransactionObj'}->id);
+ }
+
+ #If it's applicable, prepare and commit it
+ $RT::Logger->debug("Now preparing scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
+ unless ( $self->Prepare( TicketObj => $args{'TicketObj'},
+ TransactionObj => $ApplicableTransactionObj )
+ ) {
+ return undef;
+ }
+
+ $RT::Logger->debug("Now commiting scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
+ unless ( $self->Commit( TicketObj => $args{'TicketObj'},
+ TransactionObj => $ApplicableTransactionObj)
+ ) {
+ return undef;
+ }
+
+ $RT::Logger->debug("We actually finished scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
+ return (1);
+
+}
+
+# }}}
+
+# {{{ sub IsApplicable
+
+=head2 IsApplicable
+
+Calls the Condition object\'s IsApplicable method
+
+Upon success, returns the applicable Transaction object.
+Otherwise, undef is returned.
+
+If the Scrip is in the TransactionCreate Stage (the usual case), only test
+the associated Transaction object to see if it is applicable.
+
+For Scrips in the TransactionBatch Stage, test all Transaction objects
+created during the Ticket object's lifetime, and returns the first one
+that is applicable.
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ my $return;
+ eval {
+
+ my @Transactions;
+
+ if ( $self->Stage eq 'TransactionCreate') {
+ # Only look at our current Transaction
+ @Transactions = ( $args{'TransactionObj'} );
+ }
+ elsif ( $self->Stage eq 'TransactionBatch') {
+ # Look at all Transactions in this Batch
+ @Transactions = @{ $args{'TicketObj'}->TransactionBatch || [] };
+ }
+ else {
+ $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage );
+ return (undef);
+ }
+ my $ConditionObj = $self->ConditionObj;
+ foreach my $TransactionObj ( @Transactions ) {
+ # in TxnBatch stage we can select scrips that are not applicable to all txns
+ my $txn_type = $TransactionObj->Type;
+ next unless( $ConditionObj->ApplicableTransTypes =~ /(?:^|,)(?:Any|\Q$txn_type\E)(?:,|$)/i );
+ # Load the scrip's Condition object
+ $ConditionObj->LoadCondition(
+ ScripObj => $self,
+ TicketObj => $args{'TicketObj'},
+ TransactionObj => $TransactionObj,
+ );
+
+ if ( $ConditionObj->IsApplicable() ) {
+ # We found an application Transaction -- return it
+ $return = $TransactionObj;
+ last;
+ }
+ }
+ };
+ if ($@) {
+ $RT::Logger->error( "Scrip IsApplicable " . $self->Id . " died. - " . $@ );
+ return (undef);
+ }
+
+ return ($return);
+
+}
+
+# }}}
+
+# {{{ SUb Prepare
+
+=head2 Prepare
+
+Calls the action object's prepare method
+
+=cut
+
+sub Prepare {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ my $return;
+ eval {
+ $self->ActionObj->LoadAction( ScripObj => $self,
+ TicketObj => $args{'TicketObj'},
+ TransactionObj => $args{'TransactionObj'},
+ );
+
+ $return = $self->ActionObj->Prepare();
+ };
+ if ($@) {
+ $RT::Logger->error( "Scrip Prepare " . $self->Id . " died. - " . $@ );
+ return (undef);
+ }
+ unless ($return) {
+ }
+ return ($return);
+}
+
+# }}}
+
+# {{{ sub Commit
+
+=head2 Commit
+
+Calls the action object's commit method
+
+=cut
+
+sub Commit {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ my $return;
+ eval {
+ $return = $self->ActionObj->Commit();
+ };
+
+#Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
+# may have changed
+ $args{'TicketObj'}->Load( $args{'TicketObj'}->Id );
+
+ if ($@) {
+ $RT::Logger->error( "Scrip Commit " . $self->Id . " died. - " . $@ );
+ return (undef);
+ }
+
+ # Not destroying or weakening hte Action and Condition here could cause a
+ # leak
+
+ return ($return);
+}
+
+# }}}
+
+# }}}
+
+# {{{ ACL related methods
+
+# {{{ sub _Set
+
+# does an acl check and then passes off the call
+sub _Set {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
+ $RT::Logger->debug(
+ "CurrentUser can't modify Scrips for " . $self->Queue . "\n" );
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ return $self->__Set(@_);
+}
+
+# }}}
+
+# {{{ sub _Value
+# does an acl check and then passes off the call
+sub _Value {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ShowScrips') ) {
+ $RT::Logger->debug( "CurrentUser can't modify Scrips for "
+ . $self->__Value('Queue')
+ . "\n" );
+ return (undef);
+ }
+
+ return $self->__Value(@_);
+}
+
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
+=head2 CurrentUserHasRight
+
+Helper menthod for HasRight. Presets Principal to CurrentUser then
+calls HasRight.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+ return ( $self->HasRight( Principal => $self->CurrentUser->UserObj,
+ Right => $right ) );
+
+}
+
+# }}}
+
+# {{{ sub HasRight
+
+=head2 HasRight
+
+Takes a param-hash consisting of "Right" and "Principal" Principal is
+an RT::User object or an RT::CurrentUser object. "Right" is a textual
+Right string that applies to Scrips.
+
+=cut
+
+sub HasRight {
+ my $self = shift;
+ my %args = ( Right => undef,
+ Principal => undef,
+ @_ );
+
+ if ( ( defined $self->SUPER::_Value('Queue') )
+ and ( $self->SUPER::_Value('Queue') != 0 ) ) {
+ return ( $args{'Principal'}->HasRight( Right => $args{'Right'},
+ Object => $self->QueueObj ) );
+
+ }
+ else {
+ return ( $args{'Principal'}
+ ->HasRight( Object => $RT::System, Right => $args{'Right'} ) );
+ }
+}
+
+# }}}
+
+# }}}
+
+1;
+