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;
11 my $log = 'OpenSRF::Utils::Logger';
17 # if called as an instance method
18 unshift(@events,$class) if ref($class);
26 $_->update_state('invalid');
38 my $nochanges = shift; # no guarantees, yet...
39 $class = ref($class) || $class;
41 my $standalone = $editor ? 0 : 1;
42 $editor ||= new_editor();
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 );
52 my $self = bless { id => $id, editor => $editor, standalone => $standalone, nochanges => $nochanges } => $class;
61 return $self if ($self->event);
64 $self->environment( {} );
67 $log->error("No Event ID provided");
68 die "No Event ID provided";
71 return $self if (!$self->id);
73 if ($self->standalone) {
74 $self->editor->xact_begin || return undef;
78 $self->editor->retrieve_action_trigger_event([
82 atev => [ qw/event_def/ ],
83 atevdef => [ qw/hook env params/ ]
89 if ($self->standalone) {
90 $self->editor->xact_rollback || return undef;
93 $self->user_data(OpenSRF::Utils::JSON->JSON2perl( $self->event->user_data ))
94 if (defined( $self->event->user_data ));
96 if ($self->event->state eq 'valid') {
98 } elsif ($self->event->state eq 'invalid') {
100 } elsif ($self->event->state eq 'reacting') {
102 } elsif ($self->event->state eq 'reacted') {
105 } elsif ($self->event->state eq 'cleaning') {
108 } elsif ($self->event->state eq 'complete') {
112 } elsif ($self->event->state eq 'error') {
118 unless ($self->nochanges) {
119 $self->update_state('found') || die 'Unable to update event state';
122 my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type );
124 my $meth = "retrieve_" . $class;
125 $meth =~ s/Fieldmapper:://;
128 if ($self->standalone) {
129 $self->editor->xact_begin || return undef;
132 $self->target( $self->editor->$meth( $self->event->target ) );
134 if ($self->standalone) {
135 $self->editor->xact_rollback || return undef;
138 unless ($self->target) {
139 $self->update_state('invalid') unless $self->nochanges;
148 my $env = shift || $self->environment;
150 return $self if (defined $self->cleanedup);
152 if (defined $self->reacted) {
153 $self->update_state( 'cleaning') || die 'Unable to update event state';
155 my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure;
158 OpenILS::Application::Trigger::ModRunner::Cleanup
159 ->new( $cleanup, $env)
167 $log->error("Event cleanup failed with ". shift() );
168 $self->update_state( 'error' ) || die 'Unable to update event state';
171 if ($self->cleanedup) {
172 $self->update_state( 'complete' ) || die 'Unable to update event state';
174 $self->update_state( 'error' ) || die 'Unable to update event state';
178 $self->{cleanedup} = undef;
185 my $env = shift || $self->environment;
187 return $self if (defined $self->reacted);
190 if ($self->event->event_def->group_field) { # can't react individually to a grouped definition
191 $self->{reacted} = undef;
193 $self->update_state( 'reacting') || die 'Unable to update event state';
195 my $reactor = OpenILS::Application::Trigger::ModRunner::Reactor->new(
196 $self->event->event_def->reactor,
200 $self->reacted( $reactor->run->final_result);
202 if ($env->{usr_message}{usr} && $env->{usr_message}{template}) {
203 my $message_template_output =
204 $reactor->pass('ProcessMessage')->run->final_result;
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');
214 if ($self->editor->xact_begin) {
215 if ($self->editor->create_actor_usr_message( $usr_message )) {
216 $self->editor->xact_commit;
218 $self->editor->xact_rollback;
225 $log->error("Event reacting failed with ". shift() );
226 $self->update_state( 'error' ) || die 'Unable to update event state';
229 if (defined $self->reacted) {
230 $self->update_state( 'reacted' ) || die 'Unable to update event state';
232 $self->update_state( 'error' ) || die 'Unable to update event state';
236 $self->{reacted} = undef;
244 return $self if (defined $self->valid);
246 if ($self->build_environment->environment->{complete}) {
247 $self->update_state( 'validating') || die 'Unable to update event state';
250 OpenILS::Application::Trigger::ModRunner::Validator
251 ->new( $self->event->event_def->validator, $self->environment )
256 $log->error("Event validation failed with ". shift() );
257 $self->update_state( 'error' ) || die 'Unable to update event state';
260 if (defined $self->valid) {
262 $self->update_state( 'valid' ) || die 'Unable to update event state';
264 $self->update_state( 'invalid' ) || die 'Unable to update event state';
267 $self->update_state( 'error' ) || die 'Unable to update event state';
270 $self->{valid} = undef
276 sub revalidate_test {
279 if ($self->build_environment->environment->{complete}) {
282 OpenILS::Application::Trigger::ModRunner::Validator->new(
283 $self->event->event_def->validator,
288 $log->error("Event revalidation failed with ". shift());
291 return 1 if defined $self->valid and $self->valid;
296 "revalidate: could not build environment for event " .
304 return undef unless (ref $self);
307 $self->{cleanedup} = $c if (defined $c);
308 return $self->{cleanedup};
313 return undef unless (ref $self);
316 $self->{user_data} = $r if (defined $r);
317 return $self->{user_data};
322 return undef unless (ref $self);
325 $self->{reacted} = $r if (defined $r);
326 return $self->{reacted};
331 return undef unless (ref $self);
334 $self->{valid} = $v if (defined $v);
335 return $self->{valid};
340 return undef unless (ref $self);
343 $self->{event} = $e if (defined $e);
344 return $self->{event};
349 return undef unless (ref $self);
352 $self->{id} = $i if (defined $i);
358 return undef unless (ref $self);
361 $self->{environment} = $e if (defined $e);
362 return $self->{environment};
367 return undef unless (ref $self);
370 $self->{editor} = $e if (defined $e);
371 return $self->{editor};
375 # no guarantees, yet.
377 return undef unless (ref $self);
380 $self->{nochanges} = $e if (defined $e);
381 return $self->{nochanges};
386 return undef unless (ref $self);
388 die 'Cannot unfind a reacted event' if (defined $self->reacted);
390 $self->update_state( 'pending' ) || die 'Unable to update event state';
392 $self->{event} = undef;
393 $self->{environment} = undef;
399 return undef unless (ref $self);
402 $self->{target} = $t if (defined $t);
403 return $self->{target};
408 return undef unless (ref $self);
411 $self->{standalone} = $t if (defined $t);
412 return $self->{standalone};
417 return undef unless ($self && ref $self);
420 return undef unless ($state);
424 if ($self->standalone) {
425 $self->editor->xact_begin || return undef;
428 my $e = $self->editor->retrieve_action_trigger_event( $self->id );
430 $log->error( "Could not retrieve object ".$self->id." for update" ) if (!$e);
434 if ($fields && ref($fields)) {
435 $e->$_($$fields{$_}) for (keys %$fields);
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( $$ );
444 $e->clear_start_time() if ($e->state eq 'pending');
445 $e->complete_time( 'now' ) if ($e->state eq 'complete');
447 my $ok = $self->editor->update_action_trigger_event( $e );
449 $self->editor->xact_rollback if ($self->standalone);
450 $log->error( "Update of event ".$self->id." failed" );
453 $e = $self->editor->data;
454 $e = $self->editor->retrieve_action_trigger_event( $e ) if (!ref($e));
456 $log->error( "Update of event ".$self->id." did not return an object" );
459 $log->info( "Update of event ".$e->id." suceeded" );
460 $ok = $self->editor->xact_commit if ($self->standalone);
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 );
473 my $current_environment;
475 sub build_environment {
477 return $self if ($self->environment->{complete});
479 $self->update_state( 'collecting') || die 'Unable to update event state';
483 my $compartment = new Safe;
484 $compartment->permit(':default','require','dofile','caller');
485 $compartment->share('$current_environment');
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;
495 my ($usr_locale, $alt_templates, $query, $query_result, $new_template_id);
496 my $reactor = $self->environment->{event}->event_def->reactor;
498 select => { atevalt => ['id', 'locale'] },
501 event_def => $self->environment->{event}->event_def->id,
505 my $e = new_editor(xact=>1);
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;
513 select => { au => ['locale'] },
515 where => { id => $self->environment->{event}->target }
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};
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};
533 if ($new_template_id) {
535 select => { atevalt => ['template','message_template','message_title'] },
537 where => { id => $new_template_id }
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};
546 $current_environment = $self->environment;
548 $self->environment->{params}{ $_->param } = $compartment->reval($_->value) for ( @{$self->event->event_def->params} );
550 for my $e ( @{$self->event->event_def->env} ) {
552 @path = split(/\./, $e->path) if ($e->path);
553 @label = split(/\./, $e->label) if ($e->label);
555 $self->_object_by_path( $self->target, $e->collector, \@label, \@path );
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);
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 );
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 );
572 $self->_object_by_path( $self->event->event_def, undef, [qw/usr_message sending_lib/], ['owner'] );
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 );
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];
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;
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 );
598 $self->_object_by_path( $self->event->event_def, undef, [qw/context org/], ['owner'] );
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 );
607 $self->event->state, {
608 'context_item' => $self->environment->{context}->{item}
609 ? $self->environment->{context}->{item}->id
611 'context_user' => $self->environment->{context}->{usr}
612 ? $self->environment->{context}->{usr}->id
614 'context_library' => $self->environment->{context}->{org}
615 ? $self->environment->{context}->{org}->id
617 'context_bib' => $self->environment->{context}->{bib}
621 $self->environment->{complete} = 1;
623 $log->error( shift() );
624 $self->update_state( 'error' ) || die 'Unable to update event state';
627 if ($self->environment->{complete}) {
628 $self->update_state( 'collected' ) || die 'Unable to update event state';
630 $self->update_state( 'error' ) || die 'Unable to update event state';
636 sub _fm_class_by_hint {
641 OpenILS::Application->publish_fieldmapper->{$_}->{hint} eq $hint
642 } keys %{ OpenILS::Application->publish_fieldmapper };
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};
662 delete $$fhash{$ffield};
664 delete $$hhash{$step};
666 delete $$shash{$fhint};
668 delete $$phash{$path};
670 delete $_object_by_path_cache{$did};
674 sub _object_by_path {
677 my $collector = shift;
685 $ed = new_editor(xact=>1);
689 my $step = shift(@$path);
691 my $fhint = OpenILS::Application->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
692 my $fclass = $self->_fm_class_by_hint( $fhint );
694 OpenSRF::EX::ERROR->throw(
695 "$step is not a field on ".$context->class_name." Please repair the environment.")
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};
701 my $meth = 'retrieve_';
704 if ($rtype ne 'has_a') {
707 $lfield = $context->Identity;
711 $meth =~ s/Fieldmapper:://;
714 my $obj = $context->$step();
717 sprintf "_object_by_path(): meth=%s, obj=%s, multi=%s, step=%s, lfield=%s",
718 map {defined($_)? $_ : ''} ($meth, $obj, $multi, $step, $lfield)
723 my $lval = $context->$lfield();
727 my $def_id = $self->event->event_def->id;
728 my $str_path = join('.', @$path);
730 my @params = (($multi) ? { $ffield => $lval } : $lval);
731 @params = ([@params], {substream => 1}) if $meth =~ /^search/;
733 $obj = $_object_by_path_cache{$def_id}{$str_path}{$fhint}{$step}{$ffield}{$lval} ||
736 OpenILS::Application->publish_fieldmapper->{$fclass}{controller}
737 }) ? $ed : ($red ||= new_rstore_editor(xact=>1))
740 $_object_by_path_cache{$def_id}{$str_path}{$fhint}{$step}{$ffield}{$lval} ||= $obj;
748 $obj_list = [$obj] if ($obj);
754 my @path_clone = @$path;
755 $self->_object_by_path( $_, $collector, $label, \@path_clone, $ed, $red );
758 $obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
759 $context->$step( $obj ) if ($obj && (!$label || !@$label));
764 my $obj_list = [$obj] if ($obj && !$multi);
765 $obj_list = $obj if ($multi);
768 for my $o ( @$obj_list ) {
770 OpenILS::Application::Trigger::ModRunner::Collector
771 ->new( $collector, $o )
777 $obj = $new_obj_list[0];
779 $obj = \@new_obj_list;
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};
791 $$node{$$label[-1]} = $obj;
793 $obj = $$obj[0] if $rtype eq 'might_have' and ref($obj) eq 'ARRAY';
794 $context->$step( $obj ) if ($obj);
800 $red->rollback if $red;