Bug 15721: (QA followup) pick the most probable first
[koha.git] / C4 / Letters.pm
index 405dfed..0d092ac 100644 (file)
@@ -4,18 +4,18 @@ package C4::Letters;
 #
 # This file is part of Koha.
 #
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
 #
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along
-# with Koha; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use strict;
 use warnings;
@@ -31,10 +31,13 @@ use C4::Log;
 use C4::SMS;
 use C4::Debug;
 use Koha::DateUtils;
+use Koha::SMS::Providers;
+
 use Date::Calc qw( Add_Delta_Days );
 use Encode;
 use Carp;
 use Koha::Email;
+use Koha::DateUtils qw( format_sqldatetime );
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
@@ -207,8 +210,7 @@ sub GetLettersAvailableForALibrary {
 our %letter;
 sub getletter {
     my ( $module, $code, $branchcode, $message_transport_type ) = @_;
-    $message_transport_type ||= 'email';
-
+    $message_transport_type //= '%';
 
     if ( C4::Context->preference('IndependentBranches')
             and $branchcode
@@ -226,7 +228,8 @@ sub getletter {
     my $sth = $dbh->prepare(q{
         SELECT *
         FROM letter
-        WHERE module=? AND code=? AND (branchcode = ? OR branchcode = '') AND message_transport_type = ?
+        WHERE module=? AND code=? AND (branchcode = ? OR branchcode = '')
+        AND message_transport_type LIKE ?
         ORDER BY branchcode DESC LIMIT 1
     });
     $sth->execute( $module, $code, $branchcode, $message_transport_type );
@@ -237,6 +240,7 @@ sub getletter {
     return { %$line };
 }
 
+
 =head2 DelLetter
 
     DelLetter(
@@ -389,25 +393,34 @@ sub SendAlerts {
     if ( $type eq 'issue' ) {
 
         # prepare the letter...
-        # search the biblionumber
+        # search the subscriptionid
         my $sth =
           $dbh->prepare(
-            "SELECT biblionumber FROM subscription WHERE subscriptionid=?");
+            "SELECT subscriptionid FROM serial WHERE serialid=?");
         $sth->execute($externalid);
-        my ($biblionumber) = $sth->fetchrow
+        my ($subscriptionid) = $sth->fetchrow
           or warn( "No subscription for '$externalid'" ),
              return;
 
+        # search the biblionumber
+        $sth =
+          $dbh->prepare(
+            "SELECT biblionumber FROM subscription WHERE subscriptionid=?");
+        $sth->execute($subscriptionid);
+        my ($biblionumber) = $sth->fetchrow
+          or warn( "No biblionumber for '$subscriptionid'" ),
+             return;
+
         my %letter;
         # find the list of borrowers to alert
-        my $alerts = getalert( '', 'issue', $externalid );
+        my $alerts = getalert( '', 'issue', $subscriptionid );
         foreach (@$alerts) {
             my $borinfo = C4::Members::GetMember('borrowernumber' => $_->{'borrowernumber'});
             my $email = $borinfo->{email} or next;
 
 #                    warn "sending issues...";
             my $userenv = C4::Context->userenv;
-            my $branchdetails = GetBranchDetail($_->{'branchcode'});
+            my $library = Koha::Libraries->find( $_->{branchcode} );
             my $letter = GetPreparedLetter (
                 module => 'serial',
                 letter_code => $letter_code,
@@ -417,6 +430,8 @@ sub SendAlerts {
                     'biblio'      => $biblionumber,
                     'biblioitems' => $biblionumber,
                     'borrowers'   => $borinfo,
+                    'subscription' => $subscriptionid,
+                    'serial' => $externalid,
                 },
                 want_librarian => 1,
             ) or return;
@@ -426,12 +441,17 @@ sub SendAlerts {
             my %mail = $message->create_message_headers(
                 {
                     to      => $email,
-                    from    => $branchdetails->{'branchemail'},
-                    replyto => $branchdetails->{'branchreplyto'},
-                    sender  => $branchdetails->{'branchreturnpath'},
-                    subject => Encode::encode( "utf8", "" . $letter->{title} ),
-                    message => $letter->{'is_html'} ? _wrap_html( Encode::encode( "utf8", $letter->{'content'} ), Encode::encode( "utf8", "" . $letter->{'title'}  ) ) : Encode::encode( "utf8", "" . $letter->{'content'} ),
-                    contenttype => $letter->{'is_html'} ? 'text/html; charset="utf-8"' : 'text/plain; charset="utf-8"',
+                    from    => $library->branchemail,
+                    replyto => $library->branchreplyto,
+                    sender  => $library->branchreturnpath,
+                    subject => Encode::encode( "UTF-8", "" . $letter->{title} ),
+                    message => $letter->{'is_html'}
+                                ? _wrap_html( Encode::encode( "UTF-8", $letter->{'content'} ),
+                                              Encode::encode( "UTF-8", "" . $letter->{'title'} ))
+                                : Encode::encode( "UTF-8", "" . $letter->{'content'} ),
+                    contenttype => $letter->{'is_html'}
+                                    ? 'text/html; charset="utf-8"'
+                                    : 'text/plain; charset="utf-8"',
                 }
             );
             sendmail(%mail) or carp $Mail::Sendmail::error;
@@ -515,15 +535,22 @@ sub SendAlerts {
             To => join( ',', @email),
             Cc             => join( ',', @cc),
             From           => $userenv->{emailaddress},
-            Subject        => Encode::encode( "utf8", "" . $letter->{title} ),
-            Message => $letter->{'is_html'} ? _wrap_html( Encode::encode( "utf8", $letter->{'content'} ), Encode::encode( "utf8", "" . $letter->{'title'}  ) ) : Encode::encode( "utf8", "" . $letter->{'content'} ),
-            'Content-Type' => $letter->{'is_html'} ? 'text/html; charset="utf-8"' : 'text/plain; charset="utf-8"',
+            Subject        => Encode::encode( "UTF-8", "" . $letter->{title} ),
+            Message => $letter->{'is_html'}
+                            ? _wrap_html( Encode::encode( "UTF-8", $letter->{'content'} ),
+                                          Encode::encode( "UTF-8", "" . $letter->{'title'} ))
+                            : Encode::encode( "UTF-8", "" . $letter->{'content'} ),
+            'Content-Type' => $letter->{'is_html'}
+                                ? 'text/html; charset="utf-8"'
+                                : 'text/plain; charset="utf-8"',
         );
 
         $mail{'Reply-to'} = C4::Context->preference('ReplytoDefault')
           if C4::Context->preference('ReplytoDefault');
         $mail{'Sender'} = C4::Context->preference('ReturnpathDefault')
           if C4::Context->preference('ReturnpathDefault');
+        $mail{'Bcc'} = $userenv->{emailaddress}
+          if C4::Context->preference("ClaimsBccCopy");
 
         unless ( sendmail(%mail) ) {
             carp $Mail::Sendmail::error;
@@ -544,13 +571,13 @@ sub SendAlerts {
     }
    # send an "account details" notice to a newly created user
     elsif ( $type eq 'members' ) {
-        my $branchdetails = GetBranchDetail($externalid->{'branchcode'});
+        my $library = Koha::Libraries->find( $externalid->{branchcode} )->unblessed;
         my $letter = GetPreparedLetter (
             module => 'members',
             letter_code => $letter_code,
             branchcode => $externalid->{'branchcode'},
             tables => {
-                'branches'    => $branchdetails,
+                'branches'    => $library,
                 'borrowers' => $externalid->{'borrowernumber'},
             },
             substitute => { 'borrowers.password' => $externalid->{'password'} },
@@ -561,12 +588,17 @@ sub SendAlerts {
         my %mail  = $email->create_message_headers(
             {
                 to      => $externalid->{'emailaddr'},
-                from    => $branchdetails->{'branchemail'},
-                replyto => $branchdetails->{'branchreplyto'},
-                sender  => $branchdetails->{'branchreturnpath'},
-                subject => Encode::encode( "utf8", "" . $letter->{'title'} ),
-                message => $letter->{'is_html'} ? _wrap_html( Encode::encode( "utf8", $letter->{'content'} ), Encode::encode( "utf8", "" . $letter->{'title'}  ) ) : Encode::encode( "utf8", "" . $letter->{'content'} ),
-                contenttype => $letter->{'is_html'} ? 'text/html; charset="utf-8"' : 'text/plain; charset="utf-8"',
+                from    => $library->{branchemail},
+                replyto => $library->{branchreplyto},
+                sender  => $library->{branchreturnpath},
+                subject => Encode::encode( "UTF-8", "" . $letter->{'title'} ),
+                message => $letter->{'is_html'}
+                            ? _wrap_html( Encode::encode( "UTF-8", $letter->{'content'} ),
+                                          Encode::encode( "UTF-8", "" . $letter->{'title'}  ) )
+                            : Encode::encode( "UTF-8", "" . $letter->{'content'} ),
+                contenttype => $letter->{'is_html'}
+                                ? 'text/html; charset="utf-8"'
+                                : 'text/plain; charset="utf-8"',
             }
         );
         sendmail(%mail) or carp $Mail::Sendmail::error;
@@ -618,6 +650,10 @@ sub GetPreparedLetter {
 
     if ($substitute) {
         while ( my ($token, $val) = each %$substitute ) {
+            if ( $token eq 'items.content' ) {
+                $val =~ s|\n|<br/>|g if $letter->{is_html};
+            }
+
             $letter->{title} =~ s/<<$token>>/$val/g;
             $letter->{content} =~ s/<<$token>>/$val/g;
        }
@@ -739,6 +775,8 @@ sub _parseletter_sth {
     ($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 verification_token = ?" :
+    ($table eq 'subscription') ? "SELECT * FROM $table WHERE subscriptionid = ?" :
+    ($table eq 'serial') ? "SELECT * FROM $table WHERE serialid = ?" :
     undef ;
     unless ($query) {
         warn "ERROR: No _parseletter_sth query for table '$table'";
@@ -756,24 +794,31 @@ sub _parseletter_sth {
     parameters :
     - $letter : a hash to letter fields (title & content useful)
     - $table : the Koha table to parse.
-    - $values : table record hashref
+    - $values_in : table record hashref
     parse all fields from a table, and replace values in title & content with the appropriate value
     (not exported sub, used only internally)
 
 =cut
 
 sub _parseletter {
-    my ( $letter, $table, $values ) = @_;
+    my ( $letter, $table, $values_in ) = @_;
+
+    # Work on a local copy of $values_in (passed by reference) to avoid side effects
+    # in callers ( by changing / formatting values )
+    my $values = $values_in ? { %$values_in } : {};
+
+    if ( $table eq 'borrowers' && $values->{'dateexpiry'} ){
+        $values->{'dateexpiry'} = format_sqldatetime( $values->{'dateexpiry'} );
+    }
 
     if ( $table eq 'reserves' && $values->{'waitingdate'} ) {
         my @waitingdate = split /-/, $values->{'waitingdate'};
 
         $values->{'expirationdate'} = '';
-        if( C4::Context->preference('ExpireReservesMaxPickUpDelay') &&
-        C4::Context->preference('ReservesMaxPickUpDelay') ) {
+        if ( 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->{'expirationdate'} = output_pref( { dt => $dt, dateonly => 1 } );
         }
 
         $values->{'waitingdate'} = output_pref({ dt => dt_from_string( $values->{'waitingdate'} ), dateonly => 1 });
@@ -786,14 +831,14 @@ sub _parseletter {
     }
 
     while ( my ($field, $val) = each %$values ) {
-        my $replacetablefield = "<<$table.$field>>";
-        my $replacefield = "<<$field>>";
         $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.
 
         $val = GetAuthorisedValueByCode ('ROADTYPE', $val, 0) if $table=~/^borrowers$/ && $field=~/^streettype$/;
+
+        # Dates replacement
         my $replacedby   = defined ($val) ? $val : '';
         if (    $replacedby
             and not $replacedby =~ m|0000-00-00|
@@ -802,19 +847,34 @@ sub _parseletter {
         {
             # If the value is XXXX-YY-ZZ[ AA:BB:CC] we assume it is a date
             my $dateonly = defined $1 ? 0 : 1; #$1 refers to the capture group wrapped in parentheses. In this case, that's the hours, minutes, seconds.
-            eval {
-                $replacedby = output_pref({ dt => dt_from_string( $replacedby ), dateonly => $dateonly });
-            };
-            warn "$replacedby seems to be a date but an error occurs on generating it ($@)" if $@;
+            my $re_dateonly_filter = qr{ $field( \s* \| \s* dateonly\s*)?>> }xms;
+
+            for my $letter_field ( qw( title content ) ) {
+                my $filter_string_used = q{};
+                if ( $letter->{ $letter_field } =~ $re_dateonly_filter ) {
+                    # We overwrite $dateonly if the filter exists and we have a time in the datetime
+                    $filter_string_used = $1 || q{};
+                    $dateonly = $1 unless $dateonly;
+                }
+                eval {
+                    $replacedby = output_pref({ dt => dt_from_string( $replacedby ), dateonly => $dateonly });
+                };
+
+                if ( $letter->{ $letter_field } ) {
+                    $letter->{ $letter_field } =~ s/\Q<<$table.$field$filter_string_used>>\E/$replacedby/g;
+                    $letter->{ $letter_field } =~ s/\Q<<$field$filter_string_used>>\E/$replacedby/g;
+                }
+            }
+        }
+        # Other fields replacement
+        else {
+            for my $letter_field ( qw( title content ) ) {
+                if ( $letter->{ $letter_field } ) {
+                    $letter->{ $letter_field }   =~ s/<<$table.$field>>/$replacedby/g;
+                    $letter->{ $letter_field }   =~ s/<<$field>>/$replacedby/g;
+                }
+            }
         }
-        ($letter->{title}  ) and do {
-            $letter->{title}   =~ s/$replacetablefield/$replacedby/g;
-            $letter->{title}   =~ s/$replacefield/$replacedby/g;
-        };
-        ($letter->{content}) and do {
-            $letter->{content} =~ s/$replacetablefield/$replacedby/g;
-            $letter->{content} =~ s/$replacefield/$replacedby/g;
-        };
     }
 
     if ($table eq 'borrowers' && $letter->{content}) {
@@ -925,7 +985,14 @@ sub SendQueuedMessages {
             _send_message_by_email( $message, $params->{'username'}, $params->{'password'}, $params->{'method'} );
         }
         elsif ( lc( $message->{'message_transport_type'} ) eq 'sms' ) {
-            _send_message_by_sms( $message );
+            if ( C4::Context->preference('SMSSendDriver') eq 'Email' ) {
+                my $member = C4::Members::GetMember( 'borrowernumber' => $message->{'borrowernumber'} );
+                my $sms_provider = Koha::SMS::Providers->find( $member->{'sms_provider_id'} );
+                $message->{to_address} .= '@' . $sms_provider->domain();
+                _send_message_by_email( $message, $params->{'username'}, $params->{'password'}, $params->{'method'} );
+            } else {
+                _send_message_by_sms( $message );
+            }
         }
     }
     return scalar( @$unsent_messages );
@@ -1132,18 +1199,18 @@ sub _send_message_by_email {
 
     my $utf8   = decode('MIME-Header', $message->{'subject'} );
     $message->{subject}= encode('MIME-Header', $utf8);
-    my $subject = encode('utf8', $message->{'subject'});
-    my $content = encode('utf8', $message->{'content'});
+    my $subject = encode('UTF-8', $message->{'subject'});
+    my $content = encode('UTF-8', $message->{'content'});
     my $content_type = $message->{'content_type'} || 'text/plain; charset="UTF-8"';
     my $is_html = $content_type =~ m/html/io;
     my $branch_email = undef;
     my $branch_replyto = undef;
     my $branch_returnpath = undef;
-    if ($member){
-        my $branchdetail = GetBranchDetail( $member->{'branchcode'} );
-        $branch_email = $branchdetail->{'branchemail'};
-        $branch_replyto = $branchdetail->{'branchreplyto'};
-        $branch_returnpath = $branchdetail->{'branchreturnpath'};
+    if ($member) {
+        my $library = Koha::Libraries->find( $member->{branchcode} );
+        $branch_email      = $library->branchemail;
+        $branch_replyto    = $library->branchreplyto;
+        $branch_returnpath = $library->branchreturnpath;
     }
     my $email = Koha::Email->new();
     my %sendmail_params = $email->create_message_headers(
@@ -1164,6 +1231,7 @@ sub _send_message_by_email {
     }
 
     _update_message_to_address($message->{'message_id'},$to_address) unless $message->{to_address}; #if initial message address was empty, coming here means that a to address was found and queue should be updated
+
     if ( sendmail( %sendmail_params ) ) {
         _set_message_status( { message_id => $message->{'message_id'},
                 status     => 'sent' } );