use C4::Log;
use C4::SMS;
use C4::Debug;
+use Koha::DateUtils;
use Date::Calc qw( Add_Delta_Days );
use Encode;
-use Unicode::Normalize;
use Carp;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
return \%letters;
}
-=head2 GetLetter( %params )
-
- retrieves the letter template
-
- %params hash:
- module => letter module, mandatory
- letter_code => letter code, mandatory
- branchcode => for letter selection, if missing default system letter taken
- Return value:
- letter fields hashref (title & content useful)
-
-=cut
-
-sub GetLetter {
- my %params = @_;
-
- my $module = $params{module} or croak "No module";
- my $letter_code = $params{letter_code} or croak "No letter_code";
- my $branchcode = $params{branchcode} || '';
-
- my $letter = getletter( $module, $letter_code, $branchcode )
- or warn( "No $module $letter_code letter"),
- return;
-
- return $letter;
-}
-
-my %letter;
+# FIXME: using our here means that a Plack server will need to be
+# restarted fairly regularly when working with this routine.
+# A better option would be to use Koha::Cache and use a cache
+# that actually works in a persistent environment, but as a
+# short-term fix, our will work.
+our %letter;
sub getletter {
my ( $module, $code, $branchcode ) = @_;
- if ( C4::Context->preference('IndependantBranches')
+
+ if ( C4::Context->preference('IndependentBranches')
and $branchcode
and C4::Context->userenv ) {
+
$branchcode = C4::Context->userenv->{'branch'};
}
+ $branchcode //= '';
if ( my $l = $letter{$module}{$code}{$branchcode} ) {
return { %$l }; # deep copy
# warn "sending issues...";
my $userenv = C4::Context->userenv;
+ my $branchdetails = GetBranchDetail($_->{'branchcode'});
my $letter = GetPreparedLetter (
module => 'serial',
letter_code => $letter_code,
# ... then send mail
my %mail = (
To => $email,
- From => $email,
+ From => $branchdetails->{'branchemail'} || C4::Context->preference("KohaAdminEmailAddress"),
Subject => Encode::encode( "utf8", "" . $letter->{title} ),
Message => Encode::encode( "utf8", "" . $letter->{content} ),
'Content-Type' => 'text/plain; charset="utf8"',
FROM aqorders
LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
- LEFT JOIN biblioitems ON aqorders.biblioitemnumber=biblioitems.biblioitemnumber
+ LEFT JOIN biblioitems ON aqorders.biblionumber=biblioitems.biblionumber
LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
WHERE aqorders.ordernumber IN (
}
=head2 GetPreparedLetter( %params )
- retrieves letter template and performs substituion processing
-
%params hash:
module => letter module, mandatory
letter_code => letter code, mandatory
my $module = $params{module} or croak "No module";
my $letter_code = $params{letter_code} or croak "No letter_code";
my $branchcode = $params{branchcode} || '';
- my $tables = $params{tables};
- my $substitute = $params{substitute};
- my $repeat = $params{repeat};
my $letter = getletter( $module, $letter_code, $branchcode )
or warn( "No $module $letter_code letter"),
return;
- my $prepared_letter = GetProcessedLetter(
- module => $module,
- letter_code => $letter_code,
- letter => $letter,
- branchcode => $branchcode,
- tables => $tables,
- substitute => $substitute,
- repeat => $repeat
- );
-
- return $prepared_letter;
-}
-
-=head2 GetProcessedLetter( %params )
-
- given a letter, with possible pre-processing do standard processing
- allows one to perform letter template processing beforehand
-
- %params hash:
- module => letter module, mandatory
- letter_code => letter code, mandatory
- letter => letter, mandatory
- branchcode => for letter selection, if missing default system letter taken
- tables => a hashref with table names as keys. Values are either:
- - a scalar - primary key value
- - an arrayref - primary key values
- - a hashref - full record
- substitute => custom substitution key/value pairs
- repeat => records to be substituted on consecutive lines:
- - an arrayref - tries to guess what needs substituting by
- taking remaining << >> tokensr; not recommended
- - a hashref token => @tables - replaces <token> << >> << >> </token>
- subtemplate for each @tables row; table is a hashref as above
- want_librarian => boolean, if set to true triggers librarian details
- substitution from the userenv
- Return value:
- letter fields hashref (title & content useful)
-
-=cut
-
-sub GetProcessedLetter {
- my %params = @_;
-
- my $module = $params{module} or croak "No module";
- my $letter_code = $params{letter_code} or croak "No letter_code";
- my $letter = $params{letter} or croak "No letter";
- my $branchcode = $params{branchcode} || '';
my $tables = $params{tables};
my $substitute = $params{substitute};
my $repeat = $params{repeat};
-
$tables || $substitute || $repeat
or carp( "ERROR: nothing to substitute - both 'tables' and 'substitute' are empty" ),
return;
}
}
+ my $OPACBaseURL = C4::Context->preference('OPACBaseURL');
+ $letter->{content} =~ s/<<OPACBaseURL>>/$OPACBaseURL/go;
+
if ($want_librarian) {
# parsing librarian name
my $userenv = C4::Context->userenv;
$sth->execute( $ref ? @$param : $param );
$values = $sth->fetchrow_hashref;
+ $sth->finish();
}
_parseletter ( $letter, $table, $values );
}
}
-my %handles = ();
sub _parseletter_sth {
my $table = shift;
+ my $sth;
unless ($table) {
carp "ERROR: _parseletter_sth() called without argument (table)";
return;
}
- # check cache first
- (defined $handles{$table}) and return $handles{$table};
+ # NOTE: we used to check whether we had a statement handle cached in
+ # a %handles module-level variable. This was a dumb move and
+ # broke things for the rest of us. prepare_cached is a better
+ # way to cache statement handles anyway.
my $query =
- ($table eq 'biblio' ) ? "SELECT * FROM $table WHERE biblionumber = ?" :
- ($table eq 'biblioitems' ) ? "SELECT * FROM $table WHERE biblionumber = ?" :
- ($table eq 'items' ) ? "SELECT * FROM $table WHERE itemnumber = ?" :
- ($table eq 'issues' ) ? "SELECT * FROM $table WHERE itemnumber = ?" :
- ($table eq 'old_issues' ) ? "SELECT * FROM $table WHERE itemnumber = ? ORDER BY timestamp DESC LIMIT 1" :
- ($table eq 'reserves' ) ? "SELECT * FROM $table WHERE borrowernumber = ? and biblionumber = ?" :
- ($table eq 'borrowers' ) ? "SELECT * FROM $table WHERE borrowernumber = ?" :
- ($table eq 'branches' ) ? "SELECT * FROM $table WHERE branchcode = ?" :
- ($table eq 'suggestions' ) ? "SELECT * FROM $table WHERE suggestionid = ?" :
- ($table eq 'aqbooksellers') ? "SELECT * FROM $table WHERE id = ?" :
- ($table eq 'aqorders' ) ? "SELECT * FROM $table WHERE ordernumber = ?" :
- ($table eq 'opac_news' ) ? "SELECT * FROM $table WHERE idnew = ?" :
+ ($table eq 'biblio' ) ? "SELECT * FROM $table WHERE biblionumber = ?" :
+ ($table eq 'biblioitems' ) ? "SELECT * FROM $table WHERE biblionumber = ?" :
+ ($table eq 'items' ) ? "SELECT * FROM $table WHERE itemnumber = ?" :
+ ($table eq 'issues' ) ? "SELECT * FROM $table WHERE itemnumber = ?" :
+ ($table eq 'old_issues' ) ? "SELECT * FROM $table WHERE itemnumber = ? ORDER BY timestamp DESC LIMIT 1" :
+ ($table eq 'reserves' ) ? "SELECT * FROM $table WHERE borrowernumber = ? and biblionumber = ?" :
+ ($table eq 'borrowers' ) ? "SELECT * FROM $table WHERE borrowernumber = ?" :
+ ($table eq 'branches' ) ? "SELECT * FROM $table WHERE branchcode = ?" :
+ ($table eq 'suggestions' ) ? "SELECT * FROM $table WHERE suggestionid = ?" :
+ ($table eq 'aqbooksellers') ? "SELECT * FROM $table WHERE id = ?" :
+ ($table eq 'aqorders' ) ? "SELECT * FROM $table WHERE ordernumber = ?" :
+ ($table eq 'opac_news' ) ? "SELECT * FROM $table WHERE idnew = ?" :
+ ($table eq 'borrower_modifications') ? "SELECT * FROM $table WHERE borrowernumber = ? OR verification_token =?":
undef ;
unless ($query) {
warn "ERROR: No _parseletter_sth query for table '$table'";
return; # nothing to get
}
- unless ($handles{$table} = C4::Context->dbh->prepare($query)) {
+ unless ($sth = C4::Context->dbh->prepare_cached($query)) {
warn "ERROR: Failed to prepare query: '$query'";
return;
}
- return $handles{$table}; # now cache is populated for that $table
+ return $sth; # now cache is populated for that $table
}
=head2 _parseletter($letter, $table, $values)
=cut
-my %columns = ();
sub _parseletter {
my ( $letter, $table, $values ) = @_;
- # TEMPORARY hack until the expirationdate column is added to reserves
if ( $table eq 'reserves' && $values->{'waitingdate'} ) {
my @waitingdate = split /-/, $values->{'waitingdate'};
- $values->{'expirationdate'} = C4::Dates->new(
- sprintf(
- '%04d-%02d-%02d',
- Add_Delta_Days( @waitingdate, C4::Context->preference( 'ReservesMaxPickUpDelay' ) )
- ),
- 'iso'
- )->output();
+ $values->{'expirationdate'} = '';
+ if( C4::Context->preference('ExpireReservesMaxPickUpDelay') &&
+ C4::Context->preference('ReservesMaxPickUpDelay') ) {
+ my $dt = dt_from_string();
+ $dt->add( days => C4::Context->preference('ReservesMaxPickUpDelay') );
+ $values->{'expirationdate'} = output_pref({ dt => $dt, dateonly => 1 });
+ }
+
+ $values->{'waitingdate'} = output_pref({ dt => dt_from_string( $values->{'waitingdate'} ), dateonly => 1 });
+
}
if ($letter->{content} && $letter->{content} =~ /<<today>>/) {
- my @da = localtime();
- my $todaysdate = "$da[2]:$da[1] " . C4::Dates->today();
+ my $todaysdate = output_pref( DateTime->now() );
$letter->{content} =~ s/<<today>>/$todaysdate/go;
}
- # and get all fields from the table
-# my $columns = $columns{$table};
-# unless ($columns) {
-# $columns = $columns{$table} = C4::Context->dbh->selectcol_arrayref("SHOW COLUMNS FROM $table");
-# }
-# foreach my $field (@$columns) {
-
while ( my ($field, $val) = each %$values ) {
my $replacetablefield = "<<$table.$field>>";
my $replacefield = "<<$field>>";
- $val =~ s/\p{P}(?=$)//g if $val;
+ $val =~ s/\p{P}$// if $val && $table=~/biblio/;
+ #BZ 9886: Assuming that we want to eliminate ISBD punctuation here
+ #Therefore adding the test on biblio. This includes biblioitems,
+ #but excludes items. Removed unneeded global and lookahead.
+
my $replacedby = defined ($val) ? $val : '';
($letter->{title} ) and do {
$letter->{title} =~ s/$replacetablefield/$replacedby/g;
my $params = shift or return;
return unless exists $params->{'letter'};
- return unless exists $params->{'borrowernumber'};
+# return unless exists $params->{'borrowernumber'};
return unless exists $params->{'message_transport_type'};
my $content = $params->{letter}->{content};
return;
}
- # It was found that the some utf8 codes, cause the text to be truncated from that point onward when stored,
- # so we normalize utf8 with NFC so that mysql will store 'all' of the content in its TEXT column type
- # Note: It is also done in _add_attachments accordingly.
- $params->{'letter'}->{'title'} = NFC($params->{'letter'}->{'title'}); # subject
- $params->{'letter'}->{'content'} = NFC($params->{'letter'}->{'content'});
-
# If we have any attachments we should encode then into the body.
if ( $params->{'attachments'} ) {
$params->{'letter'} = _add_attachments(
$message->attach(
Type => $letter->{'content-type'} || 'TEXT',
Data => $letter->{'is_html'}
- ? _wrap_html($letter->{'content'}, NFC($letter->{'title'}))
- : NFC($letter->{'content'}),
+ ? _wrap_html($letter->{'content'}, $letter->{'title'})
+ : $letter->{'content'},
);
foreach my $attachment ( @$attachments ) {
-
- if ($attachment->{'content'} =~ m/text/o) { # NFC normailze any "text" related content-type attachments
- $attachment->{'content'} = NFC($attachment->{'content'});
- }
- $attachment->{'filename'} = NFC($attachment->{'filename'});
-
$message->attach(
Type => $attachment->{'type'},
Data => $attachment->{'content'},
my $message = shift or return;
my ($username, $password, $method) = @_;
- my $to_address = $message->{to_address};
+ my $member = C4::Members::GetMember( 'borrowernumber' => $message->{'borrowernumber'} );
+ my $to_address = $message->{'to_address'};
unless ($to_address) {
- my $member = C4::Members::GetMember( 'borrowernumber' => $message->{'borrowernumber'} );
unless ($member) {
warn "FAIL: No 'to_address' and INVALID borrowernumber ($message->{borrowernumber})";
_set_message_status( { message_id => $message->{'message_id'},
status => 'failed' } );
return;
}
- my $which_address = C4::Context->preference('AutoEmailPrimaryAddress');
- # If the system preference is set to 'first valid' (value == OFF), look up email address
- if ($which_address eq 'OFF') {
- $to_address = GetFirstValidEmailAddress( $message->{'borrowernumber'} );
- } else {
- $to_address = $member->{$which_address};
- }
+ $to_address = C4::Members::GetNoticeEmailAddress( $message->{'borrowernumber'} );
unless ($to_address) {
# warn "FAIL: No 'to_address' and no email for " . ($member->{surname} ||'') . ", borrowernumber ($message->{borrowernumber})";
# warning too verbose for this more common case?
my $content = encode('utf8', $message->{'content'});
my $content_type = $message->{'content_type'} || 'text/plain; charset="UTF-8"';
my $is_html = $content_type =~ m/html/io;
+
+ my $branch_email = ( $member ) ? GetBranchDetail( $member->{'branchcode'} )->{'branchemail'} : undef;
+
my %sendmail_params = (
To => $to_address,
- From => $message->{'from_address'} || C4::Context->preference('KohaAdminEmailAddress'),
+ From => $message->{'from_address'} || $branch_email || C4::Context->preference('KohaAdminEmailAddress'),
Subject => $subject,
charset => 'utf8',
Message => $is_html ? _wrap_html($content, $subject) : $content,
sub _send_message_by_sms {
my $message = shift or return;
my $member = C4::Members::GetMember( 'borrowernumber' => $message->{'borrowernumber'} );
- return unless $member->{'smsalertnumber'};
+
+ unless ( $member->{smsalertnumber} ) {
+ _set_message_status( { message_id => $message->{'message_id'},
+ status => 'failed' } );
+ return;
+ }
my $success = C4::SMS->send_sms( { destination => $member->{'smsalertnumber'},
message => $message->{'content'},