Bug 13622: Display a datetime without time in a notice
[koha.git] / C4 / Letters.pm
index 5673cbc..863da75 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;
@@ -44,7 +44,7 @@ BEGIN {
     $VERSION = 3.07.00.049;
     @ISA = qw(Exporter);
     @EXPORT = qw(
-        &GetLetters &GetPreparedLetter &GetWrappedLetter &addalert &getalert &delalert &findrelatedto &SendAlerts &GetPrintMessages &GetMessageTransportTypes
+        &GetLetters &GetLettersAvailableForALibrary &GetLetterTemplates &DelLetter &GetPreparedLetter &GetWrappedLetter &addalert &getalert &delalert &findrelatedto &SendAlerts &GetPrintMessages &GetMessageTransportTypes
     );
 }
 
@@ -75,6 +75,7 @@ sub GetLetters {
     my ($filters) = @_;
     my $module    = $filters->{module};
     my $code      = $filters->{code};
+    my $branchcode = $filters->{branchcode};
     my $dbh       = C4::Context->dbh;
     my $letters   = $dbh->selectall_arrayref(
         q|
@@ -84,14 +85,120 @@ sub GetLetters {
         |
           . ( $module ? q| AND module = ?| : q|| )
           . ( $code   ? q| AND code = ?|   : q|| )
+          . ( defined $branchcode   ? q| AND branchcode = ?|   : q|| )
           . q| GROUP BY code ORDER BY name|, { Slice => {} }
         , ( $module ? $module : () )
         , ( $code ? $code : () )
+        , ( defined $branchcode ? $branchcode : () )
     );
 
     return $letters;
 }
 
+=head2 GetLetterTemplates
+
+    my $letter_templates = GetLetterTemplates(
+        {
+            module => 'circulation',
+            code => 'my code',
+            branchcode => 'CPL', # '' for default,
+        }
+    );
+
+    Return a hashref of letter templates.
+    The key will be the message transport type.
+
+=cut
+
+sub GetLetterTemplates {
+    my ( $params ) = @_;
+
+    my $module    = $params->{module};
+    my $code      = $params->{code};
+    my $branchcode = $params->{branchcode} // '';
+    my $dbh       = C4::Context->dbh;
+    my $letters   = $dbh->selectall_hashref(
+        q|
+            SELECT module, code, branchcode, name, is_html, title, content, message_transport_type
+            FROM letter
+            WHERE module = ?
+            AND code = ?
+            and branchcode = ?
+        |
+        , 'message_transport_type'
+        , undef
+        , $module, $code, $branchcode
+    );
+
+    return $letters;
+}
+
+=head2 GetLettersAvailableForALibrary
+
+    my $letters = GetLettersAvailableForALibrary(
+        {
+            branchcode => 'CPL', # '' for default
+            module => 'circulation',
+        }
+    );
+
+    Return an arrayref of letters, sorted by name.
+    If a specific letter exist for the given branchcode, it will be retrieve.
+    Otherwise the default letter will be.
+
+=cut
+
+sub GetLettersAvailableForALibrary {
+    my ($filters)  = @_;
+    my $branchcode = $filters->{branchcode};
+    my $module     = $filters->{module};
+
+    croak "module should be provided" unless $module;
+
+    my $dbh             = C4::Context->dbh;
+    my $default_letters = $dbh->selectall_arrayref(
+        q|
+            SELECT module, code, branchcode, name
+            FROM letter
+            WHERE 1
+        |
+          . q| AND branchcode = ''|
+          . ( $module ? q| AND module = ?| : q|| )
+          . q| ORDER BY name|, { Slice => {} }
+        , ( $module ? $module : () )
+    );
+
+    my $specific_letters;
+    if ($branchcode) {
+        $specific_letters = $dbh->selectall_arrayref(
+            q|
+                SELECT module, code, branchcode, name
+                FROM letter
+                WHERE 1
+            |
+              . q| AND branchcode = ?|
+              . ( $module ? q| AND module = ?| : q|| )
+              . q| ORDER BY name|, { Slice => {} }
+            , $branchcode
+            , ( $module ? $module : () )
+        );
+    }
+
+    my %letters;
+    for my $l (@$default_letters) {
+        $letters{ $l->{code} } = $l;
+    }
+    for my $l (@$specific_letters) {
+        # Overwrite the default letter with the specific one.
+        $letters{ $l->{code} } = $l;
+    }
+
+    return [ map { $letters{$_} }
+          sort { $letters{$a}->{name} cmp $letters{$b}->{name} }
+          keys %letters ];
+
+}
+
 # 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
@@ -100,8 +207,7 @@ sub GetLetters {
 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
@@ -119,7 +225,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 );
@@ -130,6 +237,39 @@ sub getletter {
     return { %$line };
 }
 
+
+=head2 DelLetter
+
+    DelLetter(
+        {
+            branchcode => 'CPL',
+            module => 'circulation',
+            code => 'my code',
+            [ mtt => 'email', ]
+        }
+    );
+
+    Delete the letter. The mtt parameter is facultative.
+    If not given, all templates mathing the other parameters will be removed.
+
+=cut
+
+sub DelLetter {
+    my ($params)   = @_;
+    my $branchcode = $params->{branchcode};
+    my $module     = $params->{module};
+    my $code       = $params->{code};
+    my $mtt        = $params->{mtt};
+    my $dbh        = C4::Context->dbh;
+    $dbh->do(q|
+        DELETE FROM letter
+        WHERE branchcode = ?
+          AND module = ?
+          AND code = ?
+    | . ( $mtt ? q| AND message_transport_type = ?| : q|| )
+    , undef, $branchcode, $module, $code, ( $mtt ? $mtt : () ) );
+}
+
 =head2 addalert ($borrowernumber, $type, $externalid)
 
     parameters : 
@@ -250,18 +390,27 @@ 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;
@@ -278,6 +427,8 @@ sub SendAlerts {
                     'biblio'      => $biblionumber,
                     'biblioitems' => $biblionumber,
                     'borrowers'   => $borinfo,
+                    'subscription' => $subscriptionid,
+                    'serial' => $externalid,
                 },
                 want_librarian => 1,
             ) or return;
@@ -290,11 +441,14 @@ sub SendAlerts {
                     from    => $branchdetails->{'branchemail'},
                     replyto => $branchdetails->{'branchreplyto'},
                     sender  => $branchdetails->{'branchreturnpath'},
-                    subject => Encode::encode( "utf8", "" . $letter->{title} ),
-                    message =>
-                      Encode::encode( "utf8", "" . $letter->{content} ),
-                    contenttype => 'text/plain; charset="utf8"',
-
+                    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;
@@ -306,13 +460,11 @@ sub SendAlerts {
         # search the biblionumber
         my $strsth =  $type eq 'claimacquisition'
             ? qq{
-            SELECT aqorders.*,aqbasket.*,biblio.*,biblioitems.*,aqbooksellers.*,
-            aqbooksellers.id AS booksellerid
+            SELECT aqorders.*,aqbasket.*,biblio.*,biblioitems.*
             FROM aqorders
             LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
             LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
             LEFT JOIN biblioitems ON aqorders.biblionumber=biblioitems.biblionumber
-            LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
             WHERE aqorders.ordernumber IN (
             }
             : qq{
@@ -372,14 +524,22 @@ sub SendAlerts {
             want_librarian => 1,
         ) or return;
 
+        # Remove the order tag
+        $letter->{content} =~ s/<order>(.*?)<\/order>/$1/gxms;
+
         # ... then send mail
         my %mail = (
             To => join( ',', @email),
             Cc             => join( ',', @cc),
             From           => $userenv->{emailaddress},
-            Subject        => Encode::encode( "utf8", "" . $letter->{title} ),
-            Message        => Encode::encode( "utf8", "" . $letter->{content} ),
-            'Content-Type' => 'text/plain; charset="utf8"',
+            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')
@@ -426,9 +586,14 @@ sub SendAlerts {
                 from    => $branchdetails->{'branchemail'},
                 replyto => $branchdetails->{'branchreplyto'},
                 sender  => $branchdetails->{'branchreturnpath'},
-                subject => Encode::encode( "utf8", "" . $letter->{'title'} ),
-                message => Encode::encode( "utf8", "" . $letter->{'content'} ),
-                contenttype => 'text/plain; charset="utf8"'
+                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;
@@ -480,6 +645,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;
        }
@@ -561,7 +730,6 @@ sub _substitute_tables {
             $values = $param;
         }
         else {
-            my @pk;
             my $sth = _parseletter_sth($table);
             unless ($sth) {
                 warn "_parseletter_sth('$table') failed to return a valid sth.  No substitution will be done for that table.";
@@ -601,7 +769,9 @@ sub _parseletter_sth {
     ($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 = 0 OR borrowernumber = ? ) AND ( verification_token = '' OR verification_token = ? ) AND ( verification_token != '' OR borrowernumber != 0 )" :
+    ($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'";
@@ -649,14 +819,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|
@@ -665,19 +835,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}) {
@@ -995,8 +1180,8 @@ 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;