Bug 9886: ParseLetter removes too often punctuation characters from fields
[koha.git] / C4 / Letters.pm
index f646241..b8e2722 100644 (file)
@@ -29,9 +29,9 @@ use C4::Branch;
 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);
@@ -117,33 +117,6 @@ sub GetLetters {
     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;
 sub getletter {
     my ( $module, $code, $branchcode ) = @_;
@@ -440,8 +413,6 @@ sub SendAlerts {
 
 =head2 GetPreparedLetter( %params )
 
-    retrieves letter template and performs substituion processing
-
     %params hash:
       module => letter module, mandatory
       letter_code => letter code, mandatory
@@ -469,65 +440,14 @@ sub GetPreparedLetter {
     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;
@@ -540,6 +460,9 @@ sub GetProcessedLetter {
        }
     }
 
+    my $OPACBaseURL = C4::Context->preference('OPACBaseURL');
+    $letter->{content} =~ s/<<OPACBaseURL>>/$OPACBaseURL/go;
+
     if ($want_librarian) {
         # parsing librarian name
         my $userenv = C4::Context->userenv;
@@ -638,18 +561,19 @@ sub _parseletter_sth {
     # check cache first
     (defined $handles{$table}) and return $handles{$table};
     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'";
@@ -677,36 +601,30 @@ 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();
+        my $dt = dt_from_string();
+        $dt->add( days => C4::Context->preference('ReservesMaxPickUpDelay') );
+        $values->{'expirationdate'} = output_pref( $dt, undef, 1 );
+
+        $values->{'waitingdate'} = output_pref( dt_from_string( $values->{'waitingdate'} ), undef, 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;
@@ -756,7 +674,7 @@ sub EnqueueLetter {
     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};
@@ -766,12 +684,6 @@ sub EnqueueLetter {
         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(
@@ -942,17 +854,11 @@ sub _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'},
@@ -1015,13 +921,7 @@ sub _send_message_by_email {
                                    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?