]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Event.pm
LP2061136 - Stamping 1405 DB upgrade script
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / Trigger / Event.pm
1 package OpenILS::Application::Trigger::Event;
2 use strict; use warnings;
3 use OpenSRF::EX qw/:try/;
4 use OpenSRF::Utils::JSON;
5 use OpenSRF::Utils::Logger qw/$logger/;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::CStoreEditor q/:funcs/;
8 use OpenILS::Application::Trigger::ModRunner;
9 use Safe;
10
11 my $log = 'OpenSRF::Utils::Logger';
12
13 sub invalidate {
14     my $class = shift;
15     my @events = @_;
16
17     # if called as an instance method
18     unshift(@events,$class) if ref($class);
19
20     my $e = new_editor();
21     $e->xact_begin;
22
23     map {
24         $_->editor($e);
25         $_->standalone(0);
26         $_->update_state('invalid');
27     } @events;
28
29     $e->commit;
30
31     return @events;
32 }
33
34 sub new {
35     my $class = shift;
36     my $id = shift;
37     my $editor = shift;
38     my $nochanges = shift; # no guarantees, yet...
39     $class = ref($class) || $class;
40
41     my $standalone = $editor ? 0 : 1;
42     $editor ||= new_editor();
43
44     if (ref($id) && ref($id) eq $class) {
45         $id->environment->{EventProcessor} = $id
46              if ($id->environment->{complete}); # in case it came over an opensrf tube
47         $id->editor( $editor );
48         $id->standalone( $standalone );
49         return $id;
50     }
51
52     my $self = bless { id => $id, editor => $editor, standalone => $standalone, nochanges => $nochanges } => $class;
53
54     return $self->init()
55 }
56
57 sub init {
58     my $self = shift;
59     my $id = shift;
60
61     return $self if ($self->event);
62
63     $self->id( $id ); 
64     $self->environment( {} ); 
65
66     if (!$self->id) {
67         $log->error("No Event ID provided");
68         die "No Event ID provided";
69     }
70
71     return $self if (!$self->id);
72
73     if ($self->standalone) {
74         $self->editor->xact_begin || return undef;
75     }
76
77     $self->event(
78         $self->editor->retrieve_action_trigger_event([
79             $self->id, {
80                 flesh => 2,
81                 flesh_fields => {
82                     atev    => [ qw/event_def/ ],
83                     atevdef => [ qw/hook env params/ ]
84                 }
85             }
86         ])
87     );
88
89     if ($self->standalone) {
90         $self->editor->xact_rollback || return undef;
91     }
92
93     $self->user_data(OpenSRF::Utils::JSON->JSON2perl( $self->event->user_data ))
94         if (defined( $self->event->user_data ));
95
96     if ($self->event->state eq 'valid') {
97         $self->valid(1);
98     } elsif ($self->event->state eq 'invalid') {
99         $self->valid(0);
100     } elsif ($self->event->state eq 'reacting') {
101         $self->valid(1);
102     } elsif ($self->event->state eq 'reacted') {
103         $self->valid(1);
104         $self->reacted(1);
105     } elsif ($self->event->state eq 'cleaning') {
106         $self->valid(1);
107         $self->reacted(1);
108     } elsif ($self->event->state eq 'complete') {
109         $self->valid(1);
110         $self->reacted(1);
111         $self->cleanedup(1);
112     } elsif ($self->event->state eq 'error') {
113         $self->valid(0);
114         $self->reacted(0);
115         $self->cleanedup(0);
116     }
117
118     unless ($self->nochanges) {
119         $self->update_state('found') || die 'Unable to update event state';
120     }
121
122     my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type );
123     
124     my $meth = "retrieve_" . $class;
125     $meth =~ s/Fieldmapper:://;
126     $meth =~ s/::/_/;
127     
128     if ($self->standalone) {
129         $self->editor->xact_begin || return undef;
130     }
131
132     $self->target( $self->editor->$meth( $self->event->target ) );
133
134     if ($self->standalone) {
135         $self->editor->xact_rollback || return undef;
136     }
137
138     unless ($self->target) {
139         $self->update_state('invalid') unless $self->nochanges;
140         $self->valid(0);
141     }
142
143     return $self;
144 }
145
146 sub cleanup {
147     my $self = shift;
148     my $env = shift || $self->environment;
149
150     return $self if (defined $self->cleanedup);
151
152     if (defined $self->reacted) {
153         $self->update_state( 'cleaning') || die 'Unable to update event state';
154         try {
155             my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure;
156             if($cleanup) {
157                 $self->cleanedup(
158                     OpenILS::Application::Trigger::ModRunner::Cleanup
159                         ->new( $cleanup, $env)
160                         ->run
161                         ->final_result
162                 );
163             } else {
164                 $self->cleanedup(1);
165             }
166         } otherwise {
167             $log->error("Event cleanup failed with ". shift() );
168             $self->update_state( 'error' ) || die 'Unable to update event state';
169         };
170
171         if ($self->cleanedup) {
172             $self->update_state( 'complete' ) || die 'Unable to update event state';
173         } else {
174             $self->update_state( 'error' ) || die 'Unable to update event state';
175         }
176
177     } else {
178         $self->{cleanedup} = undef;
179     }
180     return $self;
181 }
182
183 sub react {
184     my $self = shift;
185     my $env = shift || $self->environment;
186
187     return $self if (defined $self->reacted);
188
189     if ($self->valid) {
190         if ($self->event->event_def->group_field) { # can't react individually to a grouped definition
191             $self->{reacted} = undef;
192         } else {
193             $self->update_state( 'reacting') || die 'Unable to update event state';
194             try {
195                 my $reactor = OpenILS::Application::Trigger::ModRunner::Reactor->new(
196                     $self->event->event_def->reactor,
197                     $env
198                 );
199
200                 $self->reacted( $reactor->run->final_result);
201
202                 if ($env->{usr_message}{usr} && $env->{usr_message}{template}) {
203                     my $message_template_output =
204                         $reactor->pass('ProcessMessage')->run->final_result;
205
206                     if ($message_template_output) {
207                         my $usr_message = Fieldmapper::actor::usr_message->new;
208                         $usr_message->title( $env->{usr_message}{title} || $self->event->event_def->name );
209                         $usr_message->message( $message_template_output );
210                         $usr_message->usr( $env->{usr_message}{usr}->id );
211                         $usr_message->sending_lib( $env->{usr_message}{sending_lib}->id );
212                         $usr_message->pub('t');
213
214                         if ($self->editor->xact_begin) {
215                             if ($self->editor->create_actor_usr_message( $usr_message )) {
216                                 $self->editor->xact_commit;
217                             } else {
218                                 $self->editor->xact_rollback;
219                             }
220                         }
221                     }
222                 }
223
224             } otherwise {
225                 $log->error("Event reacting failed with ". shift() );
226                 $self->update_state( 'error' ) || die 'Unable to update event state';
227             };
228
229             if (defined $self->reacted) {
230                 $self->update_state( 'reacted' ) || die 'Unable to update event state';
231             } else {
232                 $self->update_state( 'error' ) || die 'Unable to update event state';
233             }
234         }
235     } else {
236         $self->{reacted} = undef;
237     }
238     return $self;
239 }
240
241 sub validate {
242     my $self = shift;
243
244     return $self if (defined $self->valid);
245
246     if ($self->build_environment->environment->{complete}) {
247         $self->update_state( 'validating') || die 'Unable to update event state';
248         try {
249             $self->valid(
250                 OpenILS::Application::Trigger::ModRunner::Validator
251                     ->new( $self->event->event_def->validator, $self->environment )
252                     ->run
253                     ->final_result
254             );
255         } otherwise {
256             $log->error("Event validation failed with ". shift() );
257             $self->update_state( 'error' ) || die 'Unable to update event state';
258         };
259
260         if (defined $self->valid) {
261             if ($self->valid) {
262                 $self->update_state( 'valid' ) || die 'Unable to update event state';
263             } else {
264                 $self->update_state( 'invalid' ) || die 'Unable to update event state';
265             }
266         } else {
267             $self->update_state( 'error' ) || die 'Unable to update event state';
268         }
269     } else {
270         $self->{valid} = undef
271     }
272
273     return $self;
274 }
275  
276 sub revalidate_test {
277     my $self = shift;
278
279     if ($self->build_environment->environment->{complete}) {
280         try {
281             $self->valid(
282                 OpenILS::Application::Trigger::ModRunner::Validator->new(
283                     $self->event->event_def->validator,
284                     $self->environment
285                 )->run->final_result
286             );
287         } otherwise {
288             $log->error("Event revalidation failed with ". shift());
289         };
290
291         return 1 if defined $self->valid and $self->valid;
292         return 0;
293     }
294
295     $logger->error(
296         "revalidate: could not build environment for event " .
297         $self->event->id
298     );
299     return 0;
300 }
301  
302 sub cleanedup {
303     my $self = shift;
304     return undef unless (ref $self);
305
306     my $c = shift;
307     $self->{cleanedup} = $c if (defined $c);
308     return $self->{cleanedup};
309 }
310
311 sub user_data {
312     my $self = shift;
313     return undef unless (ref $self);
314
315     my $r = shift;
316     $self->{user_data} = $r if (defined $r);
317     return $self->{user_data};
318 }
319
320 sub reacted {
321     my $self = shift;
322     return undef unless (ref $self);
323
324     my $r = shift;
325     $self->{reacted} = $r if (defined $r);
326     return $self->{reacted};
327 }
328
329 sub valid {
330     my $self = shift;
331     return undef unless (ref $self);
332
333     my $v = shift;
334     $self->{valid} = $v if (defined $v);
335     return $self->{valid};
336 }
337
338 sub event {
339     my $self = shift;
340     return undef unless (ref $self);
341
342     my $e = shift;
343     $self->{event} = $e if (defined $e);
344     return $self->{event};
345 }
346
347 sub id {
348     my $self = shift;
349     return undef unless (ref $self);
350
351     my $i = shift;
352     $self->{id} = $i if (defined $i);
353     return $self->{id};
354 }
355
356 sub environment {
357     my $self = shift;
358     return undef unless (ref $self);
359
360     my $e = shift;
361     $self->{environment} = $e if (defined $e);
362     return $self->{environment};
363 }
364
365 sub editor {
366     my $self = shift;
367     return undef unless (ref $self);
368
369     my $e = shift;
370     $self->{editor} = $e if (defined $e);
371     return $self->{editor};
372 }
373
374 sub nochanges {
375     # no guarantees, yet.
376     my $self = shift;
377     return undef unless (ref $self);
378
379     my $e = shift;
380     $self->{nochanges} = $e if (defined $e);
381     return $self->{nochanges};
382 }
383
384 sub unfind {
385     my $self = shift;
386     return undef unless (ref $self);
387
388     die 'Cannot unfind a reacted event' if (defined $self->reacted);
389
390     $self->update_state( 'pending' ) || die 'Unable to update event state';
391     $self->{id} = undef;
392     $self->{event} = undef;
393     $self->{environment} = undef;
394     return $self;
395 }
396
397 sub target {
398     my $self = shift;
399     return undef unless (ref $self);
400
401     my $t = shift;
402     $self->{target} = $t if (defined $t);
403     return $self->{target};
404 }
405
406 sub standalone {
407     my $self = shift;
408     return undef unless (ref $self);
409
410     my $t = shift;
411     $self->{standalone} = $t if (defined $t);
412     return $self->{standalone};
413 }
414
415 sub update_state {
416     my $self = shift;
417     return undef unless ($self && ref $self);
418
419     my $state = shift;
420     return undef unless ($state);
421
422     my $fields = shift;
423
424     if ($self->standalone) {
425         $self->editor->xact_begin || return undef;
426     }
427
428     my $e = $self->editor->retrieve_action_trigger_event( $self->id );
429     if (!$e) {
430         $log->error( "Could not retrieve object ".$self->id." for update" ) if (!$e);
431         return undef;
432     }
433
434     if ($fields && ref($fields)) {
435         $e->$_($$fields{$_}) for (keys %$fields);
436     }
437
438     $log->info( "Retrieved object ".$self->id." for update" );
439     $e->start_time( 'now' ) unless $e->start_time;
440     $e->update_time( 'now' );
441     $e->update_process( $$ );
442     $e->state( $state );
443
444     $e->clear_start_time() if ($e->state eq 'pending');
445     $e->complete_time( 'now' ) if ($e->state eq 'complete');
446
447     my $ok = $self->editor->update_action_trigger_event( $e );
448     if (!$ok) {
449         $self->editor->xact_rollback if ($self->standalone);
450         $log->error( "Update of event ".$self->id." failed" );
451         return undef;
452     } else {
453         $e = $self->editor->data;
454         $e = $self->editor->retrieve_action_trigger_event( $e ) if (!ref($e));
455         if (!$e) {
456             $log->error( "Update of event ".$self->id." did not return an object" );
457             return undef;
458         }
459         $log->info( "Update of event ".$e->id." suceeded" );
460         $ok = $self->editor->xact_commit if ($self->standalone);
461     }
462
463     if ($ok) {
464         $self->event->start_time( $e->start_time );
465         $self->event->update_time( $e->update_time );
466         $self->event->update_process( $e->update_process );
467         $self->event->state( $e->state );
468     }
469
470     return $ok || undef;
471 }
472
473 my $current_environment;
474
475 sub build_environment {
476     my $self = shift;
477     return $self if ($self->environment->{complete});
478
479     $self->update_state( 'collecting') || die 'Unable to update event state';
480
481     try {
482    
483         my $compartment = new Safe;
484         $compartment->permit(':default','require','dofile','caller');
485         $compartment->share('$current_environment');
486
487         $self->environment->{EventProcessor} = $self;
488         $self->environment->{target} = $self->target;
489         $self->environment->{event} = $self->event;
490         $self->environment->{template} = $self->event->event_def->template;
491         $self->environment->{usr_message}{template} = $self->event->event_def->message_template;
492         $self->environment->{usr_message}{title} = $self->event->event_def->message_title;
493         $self->environment->{user_data} = $self->user_data;
494
495         my ($usr_locale, $alt_templates, $query, $query_result, $new_template_id);
496         my $reactor = $self->environment->{event}->event_def->reactor;
497         $query = {
498             select   => { atevalt => ['id', 'locale'] },
499             from     => 'atevalt',
500             where    => {
501                 event_def => $self->environment->{event}->event_def->id,
502                 active => 't'
503             }
504         };
505         my $e = new_editor(xact=>1);
506         if ($reactor) {
507             if (     $reactor eq 'SendEmail' 
508                   or $reactor eq 'ProcessTemplate' 
509                   or $reactor eq 'SendSMS') {
510                 $query_result = $e->json_query($query);
511                 $alt_templates = $query_result;
512                 $query = {
513                     select => { au => ['locale'] },
514                     from   => 'au',
515                     where  => { id => $self->environment->{event}->target }
516                 };
517                 $query_result = $e->json_query($query);
518                 $usr_locale = @$query_result[0]->{locale};
519                 if ($alt_templates and @$alt_templates and $usr_locale) {
520                     foreach (@$alt_templates) {
521                         if ($_->{locale} eq $usr_locale) { 
522                             $new_template_id = $_->{id};
523                             $self->environment->{tt_locale} = $_->{locale};
524                             last;
525                         } else { #attempt a lanuage if not locale match
526                             if ((split /\p{Dash}/,$_->{locale})[0] eq (split /\p{Dash}/,$usr_locale)[0]) {
527                                 $new_template_id = $_->{id};
528                                 $self->environment->{tt_locale} = $_->{locale};
529                             }
530                         }
531                     }
532                 }
533                 if ($new_template_id) {
534                     $query = {
535                         select => { atevalt => ['template','message_template','message_title'] }, 
536                         from   => 'atevalt',
537                         where  => { id => $new_template_id }
538                     };
539                     $query_result = $e->json_query($query);
540                     $self->environment->{template} = @$query_result[0]->{template};
541                     $self->environment->{usr_message}{template} = @$query_result[0]->{message_template};
542                     $self->environment->{usr_message}{title} = @$query_result[0]->{message_title};
543                 }
544             }
545         }
546         $current_environment = $self->environment;
547
548         $self->environment->{params}{ $_->param } = $compartment->reval($_->value) for ( @{$self->event->event_def->params} );
549     
550         for my $e ( @{$self->event->event_def->env} ) {
551             my (@label, @path);
552             @path = split(/\./, $e->path) if ($e->path);
553             @label = split(/\./, $e->label) if ($e->label);
554     
555             $self->_object_by_path( $self->target, $e->collector, \@label, \@path );
556         }
557
558         if ($self->event->event_def->group_field) {
559             my @group_path = split(/\./, $self->event->event_def->group_field);
560             pop(@group_path); # the last part is a field, should not get fleshed
561             my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path ) if (@group_path);
562         }
563
564         if ($self->event->event_def->message_usr_path and $self->environment->{usr_message}{template}) {
565             my @usr_path = split(/\./, $self->event->event_def->message_usr_path);
566             $self->_object_by_path( $self->target, undef, [qw/usr_message usr/], \@usr_path );
567
568             if ($self->event->event_def->message_library_path) {
569                 my @library_path = split(/\./, $self->event->event_def->message_library_path);
570                 $self->_object_by_path( $self->target, undef, [qw/usr_message sending_lib/], \@library_path );
571             } else {
572                 $self->_object_by_path( $self->event->event_def, undef, [qw/usr_message sending_lib/], ['owner'] );
573             }
574         }
575
576         if ($self->event->event_def->context_usr_path) {
577             my @usr_path = split(/\./, $self->event->event_def->context_usr_path);
578             $self->_object_by_path( $self->target, undef, [qw/context usr/], \@usr_path );
579         }
580
581         if ($self->event->event_def->context_bib_path) {
582             my @bib_path = split(/\./, $self->event->event_def->context_bib_path);
583             $self->_object_by_path( $self->target, undef, [qw/context bib/], \@bib_path );
584             if (ref $self->environment->{context}->{bib} eq 'ARRAY') {
585                 $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->[0];
586             }
587             if ($self->environment->{context}->{bib}->isa('Fieldmapper::biblio::record_entry')) {
588                 $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->id;
589             } elsif ($self->environment->{context}->{bib}->isa('Fieldmapper::reporter::hold_request_record')) {
590                 $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->bib_record;
591             }
592         }
593
594         if ($self->event->event_def->context_library_path) {
595             my @library_path = split(/\./, $self->event->event_def->context_library_path);
596             $self->_object_by_path( $self->target, undef, [qw/context org/], \@library_path );
597         } else {
598             $self->_object_by_path( $self->event->event_def, undef, [qw/context org/], ['owner'] );
599         }
600
601         if ($self->event->event_def->context_item_path) {
602             my @item_path = split(/\./, $self->event->event_def->context_item_path);
603             $self->_object_by_path( $self->target, undef, [qw/context item/], \@item_path );
604         }
605
606         $self->update_state(
607             $self->event->state, {
608                 'context_item' => $self->environment->{context}->{item}
609                     ? $self->environment->{context}->{item}->id
610                     : undef,
611                 'context_user' => $self->environment->{context}->{usr}
612                     ? $self->environment->{context}->{usr}->id
613                     : undef,
614                 'context_library' => $self->environment->{context}->{org}
615                     ? $self->environment->{context}->{org}->id
616                     : undef,
617                 'context_bib' => $self->environment->{context}->{bib}
618             }
619         );
620     
621         $self->environment->{complete} = 1;
622     } otherwise {
623         $log->error( shift() );
624         $self->update_state( 'error' ) || die 'Unable to update event state';
625     };
626
627     if ($self->environment->{complete}) {
628         $self->update_state( 'collected' ) || die 'Unable to update event state';
629     } else {
630         $self->update_state( 'error' ) || die 'Unable to update event state';
631     }
632
633     return $self;
634 }
635
636 sub _fm_class_by_hint {
637     my $self = shift;
638     my $hint = shift;
639
640     my ($class) = grep {
641         OpenILS::Application->publish_fieldmapper->{$_}->{hint} eq $hint
642     } keys %{ OpenILS::Application->publish_fieldmapper };
643
644     return $class;
645 }
646
647 my %_object_by_path_cache = ();
648 sub ClearObjectCache {
649     for my $did ( keys %_object_by_path_cache ) {
650         my $phash = $_object_by_path_cache{$did};
651         for my $path ( keys %$phash ) {
652             my $shash = $$phash{$path};
653             for my $fhint ( keys %$shash ) {
654                 my $hhash = $$shash{$fhint};
655                 for my $step ( keys %$hhash ) {
656                     my $fhash = $$hhash{$step};
657                     for my $ffield ( keys %$fhash ) {
658                         my $lhash = $$fhash{$ffield};
659                         for my $lfield ( keys %$lhash ) {
660                             delete $$lhash{$lfield};
661                         }
662                         delete $$fhash{$ffield};
663                     }
664                     delete $$hhash{$step};
665                 }
666                 delete $$shash{$fhint};
667             }
668             delete $$phash{$path};
669         }
670         delete $_object_by_path_cache{$did};
671     }
672 }
673         
674 sub _object_by_path {
675     my $self = shift;
676     my $context = shift;
677     my $collector = shift;
678     my $label = shift;
679     my $path = shift;
680     my $ed = shift;
681     my $red = shift;
682
683     my $outer = 0;
684     if (!$ed) {
685         $ed = new_editor(xact=>1);
686         $outer = 1;
687     }
688
689     my $step = shift(@$path);
690
691     my $fhint = OpenILS::Application->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
692     my $fclass = $self->_fm_class_by_hint( $fhint );
693
694     OpenSRF::EX::ERROR->throw(
695         "$step is not a field on ".$context->class_name."  Please repair the environment.")
696         unless $fhint;
697
698     my $ffield = OpenILS::Application->publish_fieldmapper->{$context->class_name}{links}{$step}{key};
699     my $rtype = OpenILS::Application->publish_fieldmapper->{$context->class_name}{links}{$step}{reltype};
700
701     my $meth = 'retrieve_';
702     my $multi = 0;
703     my $lfield = $step;
704     if ($rtype ne 'has_a') {
705         $meth = 'search_';
706         $multi = 1;
707         $lfield = $context->Identity;
708     }
709
710     $meth .= $fclass;
711     $meth =~ s/Fieldmapper:://;
712     $meth =~ s/::/_/g;
713
714     my $obj = $context->$step(); 
715
716     $logger->debug(
717         sprintf "_object_by_path(): meth=%s, obj=%s, multi=%s, step=%s, lfield=%s",
718         map {defined($_)? $_ : ''} ($meth,  $obj,   $multi,   $step,   $lfield)
719     );
720
721     if (!ref $obj) {
722
723         my $lval = $context->$lfield();
724
725         if(defined $lval) {
726
727             my $def_id = $self->event->event_def->id;
728             my $str_path = join('.', @$path);
729
730             my @params = (($multi) ? { $ffield => $lval } : $lval);
731             @params = ([@params], {substream => 1}) if $meth =~ /^search/;
732
733             $obj = $_object_by_path_cache{$def_id}{$str_path}{$fhint}{$step}{$ffield}{$lval} ||
734                 (
735                     (grep /cstore/, @{
736                         OpenILS::Application->publish_fieldmapper->{$fclass}{controller}
737                     }) ? $ed : ($red ||= new_rstore_editor(xact=>1))
738                 )->$meth(@params);
739
740             $_object_by_path_cache{$def_id}{$str_path}{$fhint}{$step}{$ffield}{$lval} ||= $obj;
741         }
742     }
743
744     if (@$path) {
745
746         my $obj_list = [];
747         if (!$multi) {
748             $obj_list = [$obj] if ($obj);
749         } else {
750             $obj_list = $obj;
751         }
752
753         for (@$obj_list) {
754             my @path_clone = @$path;
755             $self->_object_by_path( $_, $collector, $label, \@path_clone, $ed, $red );
756         }
757
758         $obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
759         $context->$step( $obj ) if ($obj && (!$label || !@$label));
760
761     } else {
762
763         if ($collector) {
764             my $obj_list = [$obj] if ($obj && !$multi);
765             $obj_list = $obj if ($multi);
766
767             my @new_obj_list;
768             for my $o ( @$obj_list ) {
769                 push @new_obj_list,
770                     OpenILS::Application::Trigger::ModRunner::Collector
771                         ->new( $collector, $o )
772                         ->run
773                         ->final_result
774             }
775
776             if (!$multi) {
777                 $obj = $new_obj_list[0];
778             } else {
779                 $obj = \@new_obj_list;
780             }
781         }
782
783         if ($label && @$label) {
784             my $node = $self->environment;
785             my $i = 0; my $max = scalar(@$label) - 1;
786             for (; $i < $max; $i++) {
787                 my $part = $$label[$i];
788                 $$node{$part} ||= {};
789                 $node = $$node{$part};
790             }
791             $$node{$$label[-1]} = $obj;
792         } else {
793             $obj = $$obj[0] if $rtype eq 'might_have' and ref($obj) eq 'ARRAY';
794             $context->$step( $obj ) if ($obj);
795         }
796     }
797
798     if ($outer) {
799         $ed->rollback;
800         $red->rollback if $red;
801     }
802     return $obj;
803 }
804
805 1;