import of rt 3.0.4
[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     $action->Load( $args{'ScripAction'} || '0' );
146     return ( 0, $self->loc( "Action [_1] not found", $args{'ScripAction'} ) )
147       unless $action->Id;
148
149     require RT::Template;
150     my $template = new RT::Template( $self->CurrentUser );
151     $template->Load( $args{'Template'}||'0' );
152     return ( 0, $self->loc('Template not found') ) unless $template->Id;
153
154     require RT::ScripCondition;
155     my $condition = new RT::ScripCondition( $self->CurrentUser );
156     $condition->Load( $args{'ScripCondition'}||'0' );
157
158     unless ( $condition->Id ) {
159         return ( 0, $self->loc('Condition not found') );
160     }
161
162     my ($id,$msg) = $self->SUPER::Create(
163         Queue                  => $args{'Queue'},
164         Template               => $template->Id,
165         ScripCondition         => $condition->id,
166         Stage                  => $args{'Stage'},
167         ScripAction            => $action->Id,
168         Description            => $args{'Description'},
169         CustomPrepareCode      => $args{'CustomPrepareCode'},
170         CustomCommitCode       => $args{'CustomCommitCode'},
171         CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
172
173     );
174     if ($id) {
175         return ( $id, $self->loc('Scrip Created') );
176     }
177     else {
178         return($id,$msg);
179     }
180 }
181
182 # }}}
183
184 # {{{ sub Delete
185
186 =head2 Delete
187
188 Delete this object
189
190 =cut
191
192 sub Delete {
193     my $self = shift;
194     
195     unless ($self->CurrentUserHasRight('ModifyScrips')) {
196         return (0, $self->loc('Permission Denied'));
197     }
198     
199     return ($self->SUPER::Delete(@_));
200 }
201 # }}}
202
203 # {{{ sub QueueObj
204
205 =head2 QueueObj
206
207 Retuns an RT::Queue object with this Scrip\'s queue
208
209 =cut
210
211 sub QueueObj {
212     my $self = shift;
213     
214     if (!$self->{'QueueObj'})  {
215         require RT::Queue;
216         $self->{'QueueObj'} = RT::Queue->new($self->CurrentUser);
217         $self->{'QueueObj'}->Load($self->__Value('Queue'));
218     }
219     return ($self->{'QueueObj'});
220 }
221
222 # }}}
223
224 # {{{ sub ActionObj
225
226
227 =head2 ActionObj
228
229 Retuns an RT::Action object with this Scrip\'s Action
230
231 =cut
232
233 sub ActionObj {
234     my $self = shift;
235     
236     unless (defined $self->{'ScripActionObj'})  {
237         require RT::ScripAction;
238         
239         $self->{'ScripActionObj'} = RT::ScripAction->new($self->CurrentUser);
240         #TODO: why are we loading Actions with templates like this. 
241         # two seperate methods might make more sense
242         $self->{'ScripActionObj'}->Load($self->ScripAction, $self->Template);
243     }
244     return ($self->{'ScripActionObj'});
245 }
246
247 # }}}
248
249 # {{{ sub ConditionObj
250
251 =head2 ConditionObj
252
253 Retuns an RT::ScripCondition object with this Scrip's IsApplicable
254
255 =cut
256
257 sub ConditionObj {
258     my $self = shift;
259     
260     unless (defined $self->{'ScripConditionObj'})  {
261         require RT::ScripCondition;
262         $self->{'ScripConditionObj'} = RT::ScripCondition->new($self->CurrentUser);
263         $self->{'ScripConditionObj'}->Load($self->ScripCondition);
264     }
265     return ($self->{'ScripConditionObj'});
266 }
267
268 # }}}
269
270 # {{{ sub TemplateObj
271 =head2 TemplateObj
272
273 Retuns an RT::Template object with this Scrip\'s Template
274
275 =cut
276
277 sub TemplateObj {
278     my $self = shift;
279     
280     unless (defined $self->{'TemplateObj'})  {
281         require RT::Template;
282             $self->{'TemplateObj'} = RT::Template->new($self->CurrentUser);
283             $self->{'TemplateObj'}->Load($self->Template);
284     }
285     return ($self->{'TemplateObj'});
286 }
287
288 # }}}
289
290
291 # {{{ Dealing with this instance of a scrip
292
293 =head2 Apply { TicketObj => undef, TransactionObj => undef}
294
295 This method instantiates the ScripCondition and ScripAction objects for a
296 single execution of this scrip. it then calls the IsApplicable method of the 
297 ScripCondition.
298 If that succeeds, it calls the Prepare method of the
299 ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
300
301 Usually, the ticket and transaction objects passed to this method
302 should be loaded by the SuperUser role
303
304 =cut
305
306
307 # {{{ sub Apply
308
309 sub Apply {
310     my $self = shift;
311     my %args = ( TicketObj      => undef,
312                  TransactionObj => undef,
313                  @_ );
314
315     # We want to make sure that if a scrip dies, we don't get
316     # hurt
317     eval {
318
319         #Load the scrip's Condition object
320         $self->ConditionObj->LoadCondition(
321                                       ScripObj       => $self,
322                                       TicketObj      => $args{'TicketObj'},
323                                       TransactionObj => $args{'TransactionObj'},
324         );
325
326         unless ( $self->IsApplicable() ) {
327             $self->ConditionObj->DESTROY;
328             return (undef);
329         }
330
331         #If it's applicable, prepare and commit it
332         $self->ActionObj->LoadAction( ScripObj       => $self,
333                                       TicketObj      => $args{'TicketObj'},
334                                       TransactionObj => $args{'TransactionObj'},
335         );
336
337         unless ( $self->Prepare() ) {
338             $RT::Logger->info(
339                           "$self: Couldn't prepare " . $self->ActionObj->Name );
340             $self->ActionObj->DESTROY();
341             $self->ConditionObj->DESTROY();
342             return (undef);
343         }
344         unless ( $self->Commit() ) {
345             $RT::Logger->info(
346                            "$self: Couldn't commit " . $self->ActionObj->Name );
347             $self->ActionObj->DESTROY();
348             $self->ConditionObj->DESTROY();
349             return (undef);
350         }
351
352         #Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
353         # may have changed
354         $args{'TicketObj'}->Load($args{'TicketObj'}->Id);
355
356         #We're done with it. lets clean up.
357         #TODO: something else isn't letting these get garbage collected. check em out.
358         $self->ActionObj->DESTROY();
359         $self->ConditionObj->DESTROY();
360         return (1);
361     };
362     if ($@) {
363         $RT::Logger->error( "Scrip " . $self->Id . " died. - " . $@ );
364     }
365
366 }
367 # }}}
368
369 # {{{ sub IsApplicable
370
371 =head2 IsApplicable
372
373 Calls the  Condition object\'s IsApplicable method
374
375 =cut
376
377 sub IsApplicable {
378     my $self = shift;
379     return ($self->ConditionObj->IsApplicable(@_));
380 }
381
382 # }}}
383
384 # {{{ sub Prepare
385
386 =head2 Prepare
387
388 Calls the action object's prepare method
389
390 =cut
391
392 sub Prepare {
393     my $self = shift;
394     $self->ActionObj->Prepare(@_);
395 }
396
397 # }}}
398
399 # {{{ sub Commit
400
401 =head2 Commit
402
403 Calls the action object's commit method
404
405 =cut
406
407 sub Commit {
408     my $self = shift;
409     $self->ActionObj->Commit(@_);
410 }
411
412 # }}}
413
414 # }}}
415
416 # {{{ sub DESTROY
417 sub DESTROY {
418     my $self = shift;
419     $self->{'ActionObj'} = undef;
420 }
421 # }}}
422
423 # {{{ ACL related methods
424
425 # {{{ sub _Set
426
427 # does an acl check and then passes off the call
428 sub _Set {
429     my $self = shift;
430     
431     unless ($self->CurrentUserHasRight('ModifyScrips')) {
432         $RT::Logger->debug("CurrentUser can't modify Scrips for ".$self->Queue."\n");
433         return (0, $self->loc('Permission Denied'));
434     }
435     return $self->__Set(@_);
436 }
437
438 # }}}
439
440 # {{{ sub _Value
441 # does an acl check and then passes off the call
442 sub _Value {
443     my $self = shift;
444     
445     unless ($self->CurrentUserHasRight('ShowScrips')) {
446         $RT::Logger->debug("CurrentUser can't modify Scrips for ".$self->__Value('Queue')."\n");
447         return (undef);
448     }
449     
450     return $self->__Value(@_);
451 }
452 # }}}
453
454 # {{{ sub CurrentUserHasRight
455
456 =head2 CurrentUserHasRight
457
458 Helper menthod for HasRight. Presets Principal to CurrentUser then 
459 calls HasRight.
460
461 =cut
462
463 sub CurrentUserHasRight {
464     my $self = shift;
465     my $right = shift;
466     return ($self->HasRight( Principal => $self->CurrentUser->UserObj,
467                              Right => $right ));
468     
469 }
470
471 # }}}
472
473 # {{{ sub HasRight
474
475 =head2 HasRight
476
477 Takes a param-hash consisting of "Right" and "Principal"  Principal is 
478 an RT::User object or an RT::CurrentUser object. "Right" is a textual
479 Right string that applies to Scrips.
480
481 =cut
482
483 sub HasRight {
484     my $self = shift;
485     my %args = ( Right => undef,
486                  Principal => undef,
487                  @_ );
488     
489     if ((defined $self->SUPER::_Value('Queue')) and ($self->SUPER::_Value('Queue') != 0)) {
490         return ( $args{'Principal'}->HasRight(
491                                                    Right => $args{'Right'},
492                                                    Object => $self->QueueObj
493                                                   ) 
494                );
495         
496     }
497     else {
498         return( $args{'Principal'}->HasRight( Object => $RT::System, Right =>  $args{'Right'}) );
499     }
500 }
501 # }}}
502
503 # }}}
504
505 1;
506
507