1 # ---------------------------------------------------------------
2 # Copyright (C) 2005 Georgia Public Library Service
3 # Bill Erickson <billserickson@gmail.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
17 package OpenILS::Application::Circ::HoldNotify;
18 use base qw/OpenILS::Application/;
19 use strict; use warnings;
20 use OpenSRF::EX qw(:try);
21 use vars q/$AUTOLOAD/;
23 use OpenSRF::Utils::JSON;
24 use OpenSRF::Utils::Logger qw(:logger);
25 use OpenILS::Utils::CStoreEditor q/:funcs/;
26 use OpenSRF::Utils::SettingsClient;
27 use OpenILS::Application::AppUtils;
28 use OpenILS::Const qw/:const/;
29 use OpenILS::Utils::Fieldmapper;
32 use OpenSRF::EX qw/:try/;
33 my $U = 'OpenILS::Application::AppUtils';
38 __PACKAGE__->register_method(
39 method => 'send_email_notify_pub',
40 api_name => 'open-ils.circ.send_hold_notify.email',
44 sub send_email_notify_pub {
45 my( $self, $conn, $auth, $hold_id ) = @_;
46 my $e = new_editor(authtoken => $auth);
47 return $e->event unless $e->checkauth;
48 return $e->event unless $e->allowed('CREATE_HOLD_NOTIFICATION');
49 my $notifier = __PACKAGE__->new(requestor => $e->requestor, hold_id => $hold_id);
50 return $notifier->event if $notifier->event;
51 my $stat = $notifier->send_email_notify;
52 # $e->commit if $stat == '1';
60 # ---------------------------------------------------------------
61 # Define the notifier object
62 # ---------------------------------------------------------------
64 my @AUTOLOAD_FIELDS = qw/
79 my $type = ref($self) or die "$self is not an object";
84 unless (grep { $_ eq $name } @AUTOLOAD_FIELDS) {
85 $logger->error("hold_notify: $type: invalid autoload field: $name");
86 die "$type: invalid autoload field: $name\n"
91 *{"${type}::${name}"} = sub {
94 $s->{$name} = $v if defined $v;
98 return $self->$name($data);
103 my( $class, %args ) = @_;
104 $class = ref($class) || $class;
105 my $self = bless( {}, $class );
106 $self->editor( new_editor( xact => 1, requestor => $args{requestor} ));
107 $logger->debug("circulator: creating new hold-notifier with requestor ".
108 $self->editor->requestor->id);
109 $self->fetch_data($args{hold_id});
113 sub send_email_notify {
116 my $sc = OpenSRF::Utils::SettingsClient->new;
117 my $setting = $sc->config_value(
118 qw/ apps open-ils.circ app_settings notify_hold email / );
120 $logger->debug("hold_notify: email enabled setting = $setting");
122 if( !$setting or $setting ne 'true' ) {
123 $self->editor->rollback;
124 $logger->info("hold_notify: not sending hold notify - email notifications disabled");
128 unless ($U->is_true($self->hold->email_notify)) {
129 $self->editor->rollback;
130 $logger->info("hold_notify: not sending hold notification because email_notify is false");
134 unless( $self->patron->email and $self->patron->email =~ /.+\@.+/ ) { # see if it's remotely email-esque
135 $self->editor->rollback;
136 return OpenILS::Event->new('PATRON_NO_EMAIL_ADDRESS');
139 $logger->info("hold_notify: attempting email notify on hold ".$self->hold->id);
141 my $sclient = OpenSRF::Utils::SettingsClient->new;
142 $self->settings_client($sclient);
143 my $template = $sclient->config_value('email_notify', 'template');
144 my $str = $self->flesh_template($self->load_template($template));
147 $self->editor->rollback;
148 $logger->error("hold_notify: No email notify template found - cannot notify");
152 my $reqr = $self->editor->requestor;
153 $self->editor->rollback; # we're done with this transaction
155 return 0 unless $self->send_email($str);
157 # ------------------------------------------------------------------
158 # If the hold email takes too long to send, the existing editor
159 # transaction may have timed out. Create a one-off editor to write
160 # the notification to the DB.
161 # ------------------------------------------------------------------
162 my $we = new_editor(xact=>1, requestor=>$reqr);
164 my $notify = Fieldmapper::action::hold_notification->new;
165 $notify->hold($self->hold->id);
166 $notify->notify_staff($we->requestor->id);
167 $notify->notify_time('now');
168 $notify->method('email');
170 $we->create_action_hold_notification($notify)
171 or return $we->die_event;
178 my( $self, $text ) = @_;
180 # !!! $self->editor xact has been rolled back before we get here
182 my $smtp = $self->settings_client->config_value('email_notify', 'smtp_server');
184 $logger->info("hold_notify: sending email notice to ".
185 $self->patron->email." with SMTP server $smtp");
187 my $sender = Email::Send->new({mailer => 'SMTP'});
188 $sender->mailer_args([Host => $smtp]);
194 $stat = $sender->send($text);
196 $err = $stat = shift;
197 $logger->error("hold_notify: Email notify failed with error: $err");
200 if( !$err and $stat and $stat->type eq 'success' ) {
201 $logger->info("hold_notify: successfully sent hold notification");
204 $logger->warn("hold_notify: unable to send hold notification: ".Dumper($stat));
212 # -------------------------------------------------------------------------
213 # Fetches all of the hold-related data
214 # -------------------------------------------------------------------------
218 my $e = $self->editor;
220 $logger->debug("circulator: fetching hold notify data");
222 $self->hold($e->retrieve_action_hold_request($holdid)) or return $self->event($e->event);
223 $self->copy($e->retrieve_asset_copy($self->hold->current_copy)) or return $self->event($e->event);
224 $self->volume($e->retrieve_asset_call_number($self->copy->call_number)) or return $self->event($e->event);
225 $self->title($e->retrieve_biblio_record_entry($self->volume->record)) or return $self->event($e->event);
226 $self->patron($e->retrieve_actor_user($self->hold->usr)) or return $self->event($e->event);
227 $self->pickup_lib($e->retrieve_actor_org_unit($self->hold->pickup_lib)) or return $self->event($e->event);
233 my $e = $self->editor;
235 my $patron = $self->patron;
236 my $o_name = $self->pickup_lib->name;
237 my $p_name = $patron->first_given_name .' '.$patron->family_name;
239 # try to find a suitable address for the patron
243 $e->retrieve_actor_user_address($patron->billing_address)) {
245 $e->retrieve_actor_user_address($patron->mailing_address)) {
246 $logger->warn("hold_notify: No address for user ".$patron->id);
251 unless( defined $p_addrs ) {
253 $p_addr->street1." ".
254 $p_addr->street2." ".
260 my $l_addr = $e->retrieve_actor_org_address($self->pickup_lib->holds_address);
261 my $l_addrs = (!$l_addr) ? "" :
262 $l_addr->street1." ".
263 $l_addr->street2." ".
271 if( $self->title->id == OILS_PRECAT_RECORD ) {
272 $title = ($self->copy->dummy_title) ?
273 $self->copy->dummy_title : "";
274 $author = ($self->copy->dummy_author) ?
275 $self->copy->dummy_author : "";
277 my $mods = $U->record_to_mvr($self->title);
278 $title = ($mods->title) ? $mods->title : "";
279 $author = ($mods->author) ? $mods->author : "";
284 patron_email => $self->patron->email,
285 pickup_lib_name => $o_name,
286 pickup_lib_addr => $l_addrs,
287 patron_name => $p_name,
288 patron_addr => $p_addrs,
291 call_number => $self->volume->label,
292 copy_barcode => $self->copy->barcode,
293 copy_number => $self->copy->copy_number,
301 my $template = shift;
303 unless( open(F, $template) ) {
304 $logger->error("hold_notify: Unable to open hold notification template file: $template");
308 # load the template, strip comments
315 next if $_ =~ /^\s*\#/o;
324 my( $self, $str ) = @_;
325 return undef unless $str;
327 my @time = CORE::localtime();
329 my $month = $time[4] + 1;
330 my $year = $time[5] + 1900;
332 my $data = $self->extract_data;
334 my $email = $$data{patron_email};
335 my $p_name = $$data{patron_name};
336 my $p_addr = $$data{patron_addr};
337 my $o_name = $$data{pickup_lib_name};
338 my $o_addr = $$data{pickup_lib_addr};
339 my $title = $$data{title};
340 my $author = $$data{author};
341 my $cn = $$data{call_number};
342 my $barcode = $$data{copy_barcode};
343 my $copy_number = $$data{copy_number};
345 my $sender = $self->settings_client->config_value('email_notify', 'sender_address');
346 my $reply_to = $self->pickup_lib->email;
347 $reply_to ||= $sender;
349 # if they have an org setting for bounced emails, use that as the sender address
350 if( my $set = $self->editor->search_actor_org_unit_setting(
351 { name => OILS_SETTING_ORG_BOUNCED_EMAIL,
352 org_unit => $self->pickup_lib->id } )->[0] ) {
354 my $bemail = OpenSRF::Utils::JSON->JSON2perl($set->value);
355 $sender = $bemail if $bemail;
358 $str =~ s/\${EMAIL_SENDER}/$sender/;
359 $str =~ s/\${EMAIL_RECIPIENT}/$email/;
360 $str =~ s/\${EMAIL_REPLY_TO}/$reply_to/;
361 $str =~ s/\${EMAIL_HEADERS}//;
363 $str =~ s/\${DATE}/$year-$month-$day/;
364 $str =~ s/\${LIBRARY}/$o_name/;
365 $str =~ s/\${LIBRARY_ADDRESS}/$o_addr/;
366 $str =~ s/\${PATRON_NAME}/$p_name/;
367 $str =~ s/\${PATRON_ADDRESS}/$p_addr/;
369 $str =~ s/\${TITLE}/$title/;
370 $str =~ s/\${AUTHOR}/$author/;
371 $str =~ s/\${CALL_NUMBER}/$cn/;
372 $str =~ s/\${COPY_BARCODE}/$barcode/;
373 $str =~ s/\${COPY_NUMBER}/$copy_number/;