import rt 3.0.12
[freeside.git] / rt / lib / RT / Scrip_Overlay.pm
1 # BEGIN LICENSE BLOCK
2
3 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
4
5 # (Except where explictly superceded by other copyright notices)
6
7 # This work is made available to you under the terms of Version 2 of
8 # the GNU General Public License. A copy of that license should have
9 # been provided with this software, but in any event can be snarfed
10 # from www.gnu.org.
11
12 # This work is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # General Public License for more details.
16
17 # Unless otherwise specified, all modifications, corrections or
18 # extensions to this work which alter its source code become the
19 # property of Best Practical Solutions, LLC when submitted for
20 # inclusion in the work.
21
22
23 # END LICENSE BLOCK
24 =head1 NAME
25
26   RT::Scrip - an RT Scrip object
27
28 =head1 SYNOPSIS
29
30   use RT::Scrip;
31
32 =head1 DESCRIPTION
33
34
35 =head1 METHODS
36
37 =begin testing
38
39 ok (require RT::Scrip);
40
41
42 my $q = RT::Queue->new($RT::SystemUser);
43 $q->Create(Name => 'ScripTest');
44 ok($q->Id, "Created a scriptest queue");
45
46 my $s1 = RT::Scrip->new($RT::SystemUser);
47 my ($val, $msg) =$s1->Create( Queue => $q->Id,
48              ScripAction => 'User Defined',
49              ScripCondition => 'User Defined',
50              CustomIsApplicableCode => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}',
51              CustomPrepareCode => 'return 1',
52              CustomCommitCode => '$self->TicketObj->SetPriority("87");',
53              Template => 'Blank'
54     );
55 ok($val,$msg);
56
57 my $ticket = RT::Ticket->new($RT::SystemUser);
58 my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id,
59                                     Subject => "hair on fire",
60                                     );
61 ok($tv, $tm);
62
63 ok ($ticket->Priority == '87', "Ticket priority is set right");
64
65
66 my $ticket2 = RT::Ticket->new($RT::SystemUser);
67 my ($t2v,$t2tv,$t2m) = $ticket2->Create(Queue => $q->Id,
68                                     Subject => "hair in water",
69                                     );
70 ok($t2v, $t2m);
71
72 ok ($ticket2->Priority != '87', "Ticket priority is set right");
73
74
75 =end testing
76
77 =cut
78
79 use strict;
80 no warnings qw(redefine);
81
82
83 # {{{ sub Create 
84
85 =head2 Create
86
87 Creates a new entry in the Scrips table. Takes a paramhash with:
88
89         Queue                  => 0,
90         Description            => undef,
91         Template               => undef,
92         ScripAction            => undef,
93         ScripCondition         => undef,
94         CustomPrepareCode      => undef,
95         CustomCommitCode       => undef,
96         CustomIsApplicableCode => undef,
97
98
99
100
101 Returns (retval, msg);
102 retval is 0 for failure or scrip id.  msg is a textual description of what happened.
103
104 =cut
105
106 sub Create {
107     my $self = shift;
108     my %args = (
109         Queue                  => 0,
110         Template               => 0, # name or id
111         ScripAction            => 0, # name or id
112         ScripCondition         => 0, # name or id
113         Stage                  => 'TransactionCreate',
114         Description            => undef,
115         CustomPrepareCode      => undef,
116         CustomCommitCode       => undef,
117         CustomIsApplicableCode => undef,
118
119         @_
120     );
121
122
123     if (! $args{'Queue'} ) {
124         unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'ModifyScrips') ) {
125             return ( 0, $self->loc('Permission Denied') );
126         }
127         $args{'Queue'} = 0;             # avoid undef sneaking in
128     }
129     else {
130         my $QueueObj = new RT::Queue( $self->CurrentUser );
131         $QueueObj->Load( $args{'Queue'} );
132         unless ( $QueueObj->id() ) {
133             return ( 0, $self->loc('Invalid queue') );
134         }
135         unless ( $QueueObj->CurrentUserHasRight('ModifyScrips') ) {
136             return ( 0, $self->loc('Permission Denied') );
137         }
138         $args{'Queue'} = $QueueObj->id();
139     }
140
141     #TODO +++ validate input 
142
143     require RT::ScripAction;
144     my $action = new RT::ScripAction( $self->CurrentUser );
145     if ($args{'ScripAction'}) {
146         $action->Load( $args{'ScripAction'});
147     }
148     return ( 0, $self->loc( "Action [_1] not found", $args{'ScripAction'} ) )
149       unless $action->Id;
150
151     require RT::Template;
152     my $template = new RT::Template( $self->CurrentUser );
153     if ($args{'Template'} ) {
154         $template->Load( $args{'Template'});
155     }
156     return ( 0, $self->loc('Template not found') ) unless $template->Id;
157
158     require RT::ScripCondition;
159     my $condition = new RT::ScripCondition( $self->CurrentUser );
160     if ($args{'ScripCondition'} ) {
161         $condition->Load( $args{'ScripCondition'} );
162     }
163     unless ( $condition->Id ) {
164         return ( 0, $self->loc('Condition not found') );
165     }
166
167     my ($id,$msg) = $self->SUPER::Create(
168         Queue                  => $args{'Queue'},
169         Template               => $template->Id,
170         ScripCondition         => $condition->id,
171         Stage                  => $args{'Stage'},
172         ScripAction            => $action->Id,
173         Description            => $args{'Description'},
174         CustomPrepareCode      => $args{'CustomPrepareCode'},
175         CustomCommitCode       => $args{'CustomCommitCode'},
176         CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
177
178     );
179     if ($id) {
180         return ( $id, $self->loc('Scrip Created') );
181     }
182     else {
183         return($id,$msg);
184     }
185 }
186
187 # }}}
188
189 # {{{ sub Delete
190
191 =head2 Delete
192
193 Delete this object
194
195 =cut
196
197 sub Delete {
198     my $self = shift;
199     
200     unless ($self->CurrentUserHasRight('ModifyScrips')) {
201         return (0, $self->loc('Permission Denied'));
202     }
203     
204     return ($self->SUPER::Delete(@_));
205 }
206 # }}}
207
208 # {{{ sub QueueObj
209
210 =head2 QueueObj
211
212 Retuns an RT::Queue object with this Scrip\'s queue
213
214 =cut
215
216 sub QueueObj {
217     my $self = shift;
218     
219     if (!$self->{'QueueObj'})  {
220         require RT::Queue;
221         $self->{'QueueObj'} = RT::Queue->new($self->CurrentUser);
222         $self->{'QueueObj'}->Load($self->__Value('Queue'));
223     }
224     return ($self->{'QueueObj'});
225 }
226
227 # }}}
228
229 # {{{ sub ActionObj
230
231
232 =head2 ActionObj
233
234 Retuns an RT::Action object with this Scrip\'s Action
235
236 =cut
237
238 sub ActionObj {
239     my $self = shift;
240     
241     unless (defined $self->{'ScripActionObj'})  {
242         require RT::ScripAction;
243         
244         $self->{'ScripActionObj'} = RT::ScripAction->new($self->CurrentUser);
245         #TODO: why are we loading Actions with templates like this. 
246         # two separate methods might make more sense
247         $self->{'ScripActionObj'}->Load($self->ScripAction, $self->Template);
248     }
249     return ($self->{'ScripActionObj'});
250 }
251
252 # }}}
253
254 # {{{ sub ConditionObj
255
256 =head2 ConditionObj
257
258 Retuns an RT::ScripCondition object with this Scrip's IsApplicable
259
260 =cut
261
262 sub ConditionObj {
263     my $self = shift;
264
265     unless ( defined $self->{'ScripConditionObj'} ) {
266         require RT::ScripCondition;
267         $self->{'ScripConditionObj'} =
268           RT::ScripCondition->new( $self->CurrentUser );
269         if ( $self->ScripCondition ) {
270             $self->{'ScripConditionObj'}->Load( $self->ScripCondition );
271         }
272     }
273     return ( $self->{'ScripConditionObj'} );
274 }
275
276 # }}}
277
278 # {{{ sub TemplateObj
279 =head2 TemplateObj
280
281 Retuns an RT::Template object with this Scrip\'s Template
282
283 =cut
284
285 sub TemplateObj {
286     my $self = shift;
287     
288     unless (defined $self->{'TemplateObj'})  {
289         require RT::Template;
290             $self->{'TemplateObj'} = RT::Template->new($self->CurrentUser);
291             $self->{'TemplateObj'}->Load($self->Template);
292     }
293     return ($self->{'TemplateObj'});
294 }
295
296 # }}}
297
298
299 # {{{ Dealing with this instance of a scrip
300
301 =head2 Apply { TicketObj => undef, TransactionObj => undef}
302
303 This method instantiates the ScripCondition and ScripAction objects for a
304 single execution of this scrip. it then calls the IsApplicable method of the 
305 ScripCondition.
306 If that succeeds, it calls the Prepare method of the
307 ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
308
309 Usually, the ticket and transaction objects passed to this method
310 should be loaded by the SuperUser role
311
312 =cut
313
314
315 # {{{ sub Apply
316
317 sub Apply {
318     my $self = shift;
319     my %args = ( TicketObj      => undef,
320                  TransactionObj => undef,
321                  @_ );
322
323     # We want to make sure that if a scrip dies, we don't get
324     # hurt
325     eval {
326
327         #Load the scrip's Condition object
328         $self->ConditionObj->LoadCondition(
329                                       ScripObj       => $self,
330                                       TicketObj      => $args{'TicketObj'},
331                                       TransactionObj => $args{'TransactionObj'},
332         );
333
334         unless ( $self->IsApplicable() ) {
335             $self->ConditionObj->DESTROY;
336             return (undef);
337         }
338
339         #If it's applicable, prepare and commit it
340         $self->ActionObj->LoadAction( ScripObj       => $self,
341                                       TicketObj      => $args{'TicketObj'},
342                                       TransactionObj => $args{'TransactionObj'},
343         );
344
345         unless ( $self->Prepare() ) {
346             $RT::Logger->info(
347                           "$self: Couldn't prepare " . $self->ActionObj->Name );
348             $self->ActionObj->DESTROY();
349             $self->ConditionObj->DESTROY();
350             return (undef);
351         }
352         unless ( $self->Commit() ) {
353             $RT::Logger->info(
354                            "$self: Couldn't commit " . $self->ActionObj->Name );
355             $self->ActionObj->DESTROY();
356             $self->ConditionObj->DESTROY();
357             return (undef);
358         }
359
360         #Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
361         # may have changed
362         $args{'TicketObj'}->Load($args{'TicketObj'}->Id);
363
364         #We're done with it. lets clean up.
365         #TODO: something else isn't letting these get garbage collected. check em out.
366         $self->ActionObj->DESTROY();
367         $self->ConditionObj->DESTROY();
368         return (1);
369     };
370     if ($@) {
371         $RT::Logger->error( "Scrip " . $self->Id . " died. - " . $@ );
372     }
373
374 }
375 # }}}
376
377 # {{{ sub IsApplicable
378
379 =head2 IsApplicable
380
381 Calls the  Condition object\'s IsApplicable method
382
383 =cut
384
385 sub IsApplicable {
386     my $self = shift;
387     return ($self->ConditionObj->IsApplicable(@_));
388 }
389
390 # }}}
391
392 # {{{ sub Prepare
393
394 =head2 Prepare
395
396 Calls the action object's prepare method
397
398 =cut
399
400 sub Prepare {
401     my $self = shift;
402     $self->ActionObj->Prepare(@_);
403 }
404
405 # }}}
406
407 # {{{ sub Commit
408
409 =head2 Commit
410
411 Calls the action object's commit method
412
413 =cut
414
415 sub Commit {
416     my $self = shift;
417     $self->ActionObj->Commit(@_);
418 }
419
420 # }}}
421
422 # }}}
423
424 # {{{ sub DESTROY
425 sub DESTROY {
426     my $self = shift;
427     $self->{'ActionObj'} = undef;
428 }
429 # }}}
430
431 # {{{ ACL related methods
432
433 # {{{ sub _Set
434
435 # does an acl check and then passes off the call
436 sub _Set {
437     my $self = shift;
438     
439     unless ($self->CurrentUserHasRight('ModifyScrips')) {
440         $RT::Logger->debug("CurrentUser can't modify Scrips for ".$self->Queue."\n");
441         return (0, $self->loc('Permission Denied'));
442     }
443     return $self->__Set(@_);
444 }
445
446 # }}}
447
448 # {{{ sub _Value
449 # does an acl check and then passes off the call
450 sub _Value {
451     my $self = shift;
452     
453     unless ($self->CurrentUserHasRight('ShowScrips')) {
454         $RT::Logger->debug("CurrentUser can't modify Scrips for ".$self->__Value('Queue')."\n");
455         return (undef);
456     }
457     
458     return $self->__Value(@_);
459 }
460 # }}}
461
462 # {{{ sub CurrentUserHasRight
463
464 =head2 CurrentUserHasRight
465
466 Helper menthod for HasRight. Presets Principal to CurrentUser then 
467 calls HasRight.
468
469 =cut
470
471 sub CurrentUserHasRight {
472     my $self = shift;
473     my $right = shift;
474     return ($self->HasRight( Principal => $self->CurrentUser->UserObj,
475                              Right => $right ));
476     
477 }
478
479 # }}}
480
481 # {{{ sub HasRight
482
483 =head2 HasRight
484
485 Takes a param-hash consisting of "Right" and "Principal"  Principal is 
486 an RT::User object or an RT::CurrentUser object. "Right" is a textual
487 Right string that applies to Scrips.
488
489 =cut
490
491 sub HasRight {
492     my $self = shift;
493     my %args = ( Right => undef,
494                  Principal => undef,
495                  @_ );
496     
497     if ((defined $self->SUPER::_Value('Queue')) and ($self->SUPER::_Value('Queue') != 0)) {
498         return ( $args{'Principal'}->HasRight(
499                                                    Right => $args{'Right'},
500                                                    Object => $self->QueueObj
501                                                   ) 
502                );
503         
504     }
505     else {
506         return( $args{'Principal'}->HasRight( Object => $RT::System, Right =>  $args{'Right'}) );
507     }
508 }
509 # }}}
510
511 # }}}
512
513 1;
514
515