]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm
use Safe to protect against EVIL evals coming in from the outside world -- we share...
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Trigger / Event.pm
1 package OpenILS::Application::Trigger::Event;
2 use strict; use warnings;
3 use OpenSRF::EX qw/:try/;
4
5 use OpenSRF::Utils::Logger qw/:logger/;
6
7 use OpenILS::Utils::Fieldmapper;
8 use OpenILS::Utils::CStoreEditor q/:funcs/;
9 use OpenILS::Application::Trigger::ModRunner;
10
11 use Safe;
12
13 my $log = 'OpenSRF::Utils::Logger';
14
15 sub new {
16     my $class = shift;
17     my $id = shift;
18     my $editor = shift;
19     $class = ref($class) || $class;
20
21     return $id if (ref($id) && ref($id) eq $class);
22
23     my $standalone = $editor ? 0 : 1;
24     $editor ||= new_editor();
25
26     my $self = bless { id => $id, editor => $editor, standalone => $standalone } => $class;
27
28     return $self->init()
29 }
30
31 sub init {
32     my $self = shift;
33     my $id = shift;
34
35     return $self if ($self->event);
36
37     $self->id( $id ); 
38     $self->environment( {} ); 
39
40     if (!$self->id) {
41         $log->error("No Event ID provided");
42         die "No Event ID provided";
43     }
44
45     return $self if (!$self->id);
46
47     $self->event(
48         $self->editor->retrieve_action_trigger_event([
49             $self->id, {
50                 flesh => 2,
51                 flesh_fields => {
52                     atev    => [ qw/event_def/ ],
53                     atevdef => [ qw/hook env params/ ]
54                 }
55             }
56         ])
57     );
58
59     if ($self->event->state eq 'valid') {
60         $self->valid(1);
61     } elsif ($self->event->state eq 'invalid') {
62         $self->valid(0);
63     } elsif ($self->event->state eq 'reacting') {
64         $self->valid(1);
65     } elsif ($self->event->state eq 'reacted') {
66         $self->valid(1);
67         $self->reacted(1);
68     } elsif ($self->event->state eq 'cleaning') {
69         $self->valid(1);
70         $self->reacted(1);
71     } elsif ($self->event->state eq 'complete') {
72         $self->valid(1);
73         $self->reacted(1);
74         $self->cleanedup(1);
75     } elsif ($self->event->state eq 'error') {
76         $self->valid(0);
77         $self->reacted(0);
78         $self->cleanedup(0);
79     }
80
81
82     $self->update_state('found') || die 'Unable to update event state';
83
84     my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type );
85     
86     my $meth = "retrieve_" . $class;
87     $meth =~ s/Fieldmapper:://;
88     $meth =~ s/::/_/;
89     
90     $self->target( $self->editor->$meth( $self->event->target ) );
91
92     return $self;
93 }
94
95 sub cleanup {
96     my $self = shift;
97     my $env = shift || $self->environment;
98
99     return $self if (defined $self->cleanedup);
100
101     if (defined $self->reacted) {
102         $self->update_state( 'cleaning') || die 'Unable to update event state';
103         try {
104             my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure;
105             if($cleanup) {
106                 $self->cleanedup(
107                     OpenILS::Application::Trigger::ModRunner::Cleanup
108                         ->new( $cleanup, $env)
109                         ->run
110                         ->final_result
111                 );
112             } else {
113                 $self->cleanedup(1);
114             }
115         } otherwise {
116             $log->error( shift() );
117             $self->update_state( 'error' ) || die 'Unable to update event state';
118         };
119
120         if ($self->cleanedup) {
121             $self->update_state( 'complete' ) || die 'Unable to update event state';
122         } else {
123             $self->update_state( 'error' ) || die 'Unable to update event state';
124         }
125
126     } else {
127         $self->{cleanedup} = undef;
128     }
129     return $self;
130 }
131
132 sub react {
133     my $self = shift;
134     my $env = shift || $self->environment;
135
136     return $self if (defined $self->reacted);
137
138     if ($self->valid) {
139         if ($self->event->event_def->group_field) { # can't react individually to a grouped definition
140             $self->{reacted} = undef;
141         } else {
142             $self->update_state( 'reacting') || die 'Unable to update event state';
143             try {
144                 $self->reacted(
145                     OpenILS::Application::Trigger::ModRunner::Reactor
146                         ->new( $self->event->event_def->reactor, $env )
147                         ->run
148                         ->final_result
149                 );
150             } otherwise {
151                 $log->error( shift() );
152                 $self->update_state( 'error' ) || die 'Unable to update event state';
153             };
154
155             if (defined $self->reacted) {
156                 $self->update_state( 'reacted' ) || die 'Unable to update event state';
157             } else {
158                 $self->update_state( 'error' ) || die 'Unable to update event state';
159             }
160         }
161     } else {
162         $self->{reacted} = undef;
163     }
164     return $self;
165 }
166
167 sub validate {
168     my $self = shift;
169
170     return $self if (defined $self->valid);
171
172     if ($self->build_environment->environment->{complete}) {
173         $self->update_state( 'validating') || die 'Unable to update event state';
174         try {
175             $self->valid(
176                 OpenILS::Application::Trigger::ModRunner::Validator
177                     ->new( $self->event->event_def->validator, $self->environment )
178                     ->run
179                     ->final_result
180             );
181         } otherwise {
182             $log->error( shift() );
183             $self->update_state( 'error' ) || die 'Unable to update event state';
184         };
185
186         if (defined $self->valid) {
187             if ($self->valid) {
188                 $self->update_state( 'valid' ) || die 'Unable to update event state';
189             } else {
190                 $self->update_state( 'invalid' ) || die 'Unable to update event state';
191             }
192         } else {
193             $self->update_state( 'error' ) || die 'Unable to update event state';
194         }
195     } else {
196         $self->{valid} = undef
197     }
198
199     return $self;
200 }
201  
202 sub cleanedup {
203     my $self = shift;
204     return undef unless (ref $self);
205
206     my $c = shift;
207     $self->{cleanedup} = $c if (defined $c);
208     return $self->{cleanedup};
209 }
210
211 sub reacted {
212     my $self = shift;
213     return undef unless (ref $self);
214
215     my $r = shift;
216     $self->{reacted} = $r if (defined $r);
217     return $self->{reacted};
218 }
219
220 sub valid {
221     my $self = shift;
222     return undef unless (ref $self);
223
224     my $v = shift;
225     $self->{valid} = $v if (defined $v);
226     return $self->{valid};
227 }
228
229 sub event {
230     my $self = shift;
231     return undef unless (ref $self);
232
233     my $e = shift;
234     $self->{event} = $e if (defined $e);
235     return $self->{event};
236 }
237
238 sub id {
239     my $self = shift;
240     return undef unless (ref $self);
241
242     my $i = shift;
243     $self->{id} = $i if (defined $i);
244     return $self->{id};
245 }
246
247 sub environment {
248     my $self = shift;
249     return undef unless (ref $self);
250
251     my $e = shift;
252     $self->{environment} = $e if (defined $e);
253     return $self->{environment};
254 }
255
256 sub editor {
257     my $self = shift;
258     return undef unless (ref $self);
259
260     my $e = shift;
261     $self->{editor} = $e if (defined $e);
262     return $self->{editor};
263 }
264
265 sub unfind {
266     my $self = shift;
267     return undef unless (ref $self);
268
269     die 'Cannot unfind a reacted event' if (defined $self->reacted);
270
271     $self->update_state( 'pending' ) || die 'Unable to update event state';
272     $self->{id} = undef;
273     $self->{event} = undef;
274     $self->{environment} = undef;
275     return $self;
276 }
277
278 sub target {
279     my $self = shift;
280     return undef unless (ref $self);
281
282     my $t = shift;
283     $self->{target} = $t if (defined $t);
284     return $self->{target};
285 }
286
287 sub standalone {
288     my $self = shift;
289     return undef unless (ref $self);
290
291     my $t = shift;
292     $self->{standalone} = $t if (defined $t);
293     return $self->{standalone};
294 }
295
296 sub update_state {
297     my $self = shift;
298     return undef unless ($self && ref $self);
299
300     my $state = shift;
301     return undef unless ($state);
302
303     my $fields = shift;
304
305     if ($self->standalone) {
306         $self->editor->xact_begin || return undef;
307     }
308
309     my $e = $self->editor->retrieve_action_trigger_event( $self->id );
310     if (!$e) {
311         $log->error( "Could not retrieve object ".$self->id." for update" ) if (!$e);
312         return undef;
313     }
314
315     if ($fields && ref($fields)) {
316         $e->$_($$fields{$_}) for (keys %$fields);
317     }
318
319     $log->info( "Retrieved object ".$self->id." for update" );
320     $e->start_time( 'now' ) unless $e->start_time;
321     $e->update_time( 'now' );
322     $e->update_process( $$ );
323     $e->state( $state );
324
325     $e->clear_start_time() if ($e->state eq 'pending');
326
327     my $ok = $self->editor->update_action_trigger_event( $e );
328     if (!$ok) {
329         $self->editor->xact_rollback if ($self->standalone);
330         $log->error( "Update of event ".$self->id." failed" );
331         return undef;
332     } else {
333         $e = $self->editor->data;
334         $e = $self->editor->retrieve_action_trigger_event( $e ) if (!ref($e));
335         if (!$e) {
336             $log->error( "Update of event ".$self->id." did not return an object" );
337             return undef;
338         }
339         $log->info( "Update of event ".$e->id." suceeded" );
340         $ok = $self->editor->xact_commit if ($self->standalone);
341     }
342
343     if ($ok) {
344         $self->event->start_time( $e->start_time );
345         $self->event->update_time( $e->update_time );
346         $self->event->update_process( $e->update_process );
347         $self->event->state( $e->state );
348     }
349
350     return $ok || undef;
351 }
352
353 my $current_environment;
354
355 sub build_environment {
356     my $self = shift;
357     return $self if ($self->environment->{complete});
358
359     $self->update_state( 'collecting') || die 'Unable to update event state';
360
361     try {
362    
363         my $compartment = new Safe;
364         $compartment->permit(':default',':load');
365         $compartment->share('$current_environment');
366
367         $self->environment->{EventProcessor} = $self;
368         $self->environment->{target} = $self->target;
369         $self->environment->{event} = $self->event;
370         $self->environment->{template} = $self->event->event_def->template;
371
372         $current_environment = $self->environment;
373
374         $self->environment->{params}{ $_->param } = $compartment->reval($_->value) for ( @{$self->event->event_def->params} );
375     
376         for my $e ( @{$self->event->event_def->env} ) {
377             my (@label, @path);
378             @path = split(/\./, $e->path) if ($e->path);
379             @label = split(/\./, $e->label) if ($e->label);
380     
381             $self->_object_by_path( $self->target, $e->collector, \@label, \@path );
382         }
383
384         if ($self->event->event_def->group_field) {
385             my @group_path = split(/\./, $self->event->event_def->group_field);
386             my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path );
387         }
388     
389         $self->environment->{complete} = 1;
390     } otherwise {
391         $log->error( shift() );
392         $self->update_state( 'error' ) || die 'Unable to update event state';
393     };
394
395     if ($self->environment->{complete}) {
396         $self->update_state( 'collected' ) || die 'Unable to update event state';
397     } else {
398         $self->update_state( 'error' ) || die 'Unable to update event state';
399     }
400
401     return $self;
402 }
403
404 sub _fm_class_by_hint {
405     my $self = shift;
406     my $hint = shift;
407
408     my ($class) = grep {
409         Fieldmapper->publish_fieldmapper->{$_}->{hint} eq $hint
410     } keys %{ Fieldmapper->publish_fieldmapper };
411
412     return $class;
413 }
414
415 sub _object_by_path {
416     my $self = shift;
417     my $context = shift;
418     my $collector = shift;
419     my $label = shift;
420     my $path = shift;
421
422     my $step = shift(@$path);
423
424     my $fhint = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
425     my $fclass = $self->_fm_class_by_hint( $fhint );
426
427     my $ffield = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{key};
428     my $rtype = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{reltype};
429
430     my $meth = 'retrieve_';
431     my $multi = 0;
432     my $lfield = $step;
433     if ($rtype ne 'has_a') {
434         $meth = 'search_';
435         $multi = 1;
436         $lfield = $context->Identity;
437     }
438
439     $meth .= $fclass;
440     $meth =~ s/Fieldmapper:://;
441     $meth =~ s/::/_/;
442
443     my $ed = grep( /open-ils.cstore/, @{$fclass->Controller} ) ?
444             $self->editor :
445             new_rstore_editor();
446
447     my $obj = $context->$step(); 
448
449     if (!ref $obj) {
450         $obj = $ed->$meth( 
451             ($multi) ?
452                 { $ffield => $context->$lfield() } :
453                 $context->$lfield()
454         );
455     }
456
457     if (@$path) {
458
459         my $obj_list = [];
460         if (!$multi) {
461             $obj_list = [$obj] if ($obj);
462         } else {
463             $obj_list = $obj;
464         }
465
466         for (@$obj_list) {
467             my @path_clone = @$path;
468             $self->_object_by_path( $_, $collector, $label, \@path_clone );
469         }
470
471         $obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
472         $context->$step( $obj ) if ($obj && (!$label || !@$label));
473
474     } else {
475
476         if ($collector) {
477             my $obj_list = [$obj] if ($obj && !$multi);
478             $obj_list = $obj if ($multi);
479
480             my @new_obj_list;
481             for my $o ( @$obj_list ) {
482                 push @new_obj_list,
483                     OpenILS::Application::Trigger::ModRunner::Collector
484                         ->new( $collector, $o )
485                         ->run
486                         ->final_result
487             }
488
489             if (!$multi) {
490                 $obj = $new_obj_list[0];
491             } else {
492                 $obj = \@new_obj_list;
493             }
494         }
495
496         if ($label && @$label) {
497             my $node = $self->environment;
498             my $i = 0; my $max = scalar(@$label);
499             for (; $i < $max; $i++) {
500                 my $part = $$label[$i];
501                 $$node{$part} ||= {};
502                 $node = $$node{$part};
503             }
504             $$node{$$label[-1]} = $obj;
505         } else {
506             $obj = $$obj[0] if $rtype eq 'might_have';
507             $context->$step( $obj ) if ($obj);
508         }
509     }
510
511     return $obj;
512 }
513
514 1;