Bug 34924: Add 'auto_renew_final' and 'auto_unseen_final' return to CanBookBeRenewed
[koha.git] / misc / cronjobs / gather_print_notices.pl
1 #!/usr/bin/perl -w
2
3 use Modern::Perl;
4
5 use CGI; # NOT a CGI script, this is just to keep C4::Templates::gettemplate happy
6 use Koha::Script -cron;
7 use C4::Context;
8 use C4::Letters qw( GetPrintMessages );
9 use C4::Templates;
10 use File::Spec;
11 use Pod::Usage qw( pod2usage );
12 use Getopt::Long qw( GetOptions );
13 use C4::Log qw( cronlogaction );
14
15 use Koha::DateUtils qw( dt_from_string output_pref );
16 use Koha::Email;
17 use Koha::Util::OpenDocument qw( generate_ods );
18 use Koha::SMTP::Servers;
19
20 my (
21     $help,
22     $split,
23     $html,
24     $csv,
25     $ods,
26     $delimiter,
27     @letter_codes,
28     $send,
29     @emails,
30 );
31
32 my $command_line_options = join(" ",@ARGV);
33
34 $send = 1;
35 GetOptions(
36     'h|help'  => \$help,
37     's|split' => \$split,
38     'html'    => \$html,
39     'csv'     => \$csv,
40     'ods'     => \$ods,
41     'd|delimiter:s' => \$delimiter,
42     'letter_code:s' => \@letter_codes,
43     'send!'         => \$send,
44     'e|email:s'     => \@emails,
45 ) || pod2usage(1);
46
47 pod2usage(0) if $help;
48
49 my $output_directory = $ARGV[0];
50
51 if ( !$output_directory || !-d $output_directory || !-w $output_directory ) {
52     pod2usage({
53         -exitval => 1,
54         -msg => qq{\nError: You must specify a valid and writeable directory to dump the print notices in.\n},
55     });
56 }
57
58 # Default value is html
59 $html = 1 if not $html and not $csv and not $ods;
60
61 if ( $csv and @letter_codes != 1 ) {
62     pod2usage({
63         -exitval => 1,
64         -msg => qq{\nIt is not consistent to use --csv without one (and only one) letter_code\n},
65     });
66 }
67
68 if ( $ods and @letter_codes != 1 ) {
69     pod2usage({
70         -exitval => 1,
71         -msg => qq{\nIt is not consistent to use --ods without one (and only one) letter_code\n},
72     });
73 }
74
75 $delimiter ||= q|,|;
76
77 cronlogaction({ info => $command_line_options });
78
79 my $today_iso     = output_pref( { dt => dt_from_string, dateonly => 1, dateformat => 'iso' } ) ;
80 my $today_syspref = output_pref( { dt => dt_from_string, dateonly => 1 } );
81
82 my @all_messages = @{ GetPrintMessages() };
83
84 # Filter by letter_code
85 @all_messages = map {
86     my $letter_code = $_->{letter_code};
87     (
88         grep { $_ eq $letter_code } @letter_codes
89     ) ? $_ : ()
90 } @all_messages if @letter_codes;
91 exit unless @all_messages;
92
93 my ( $html_filenames, $csv_filenames, $ods_filenames );
94 $csv_filenames = print_notices({
95     messages => \@all_messages,
96     split => $split,
97     output_directory => $output_directory,
98     format => 'csv',
99 }) if $csv;
100
101 $ods_filenames = print_notices({
102     messages => \@all_messages,
103     split => $split,
104     output_directory => $output_directory,
105     format => 'ods',
106 }) if $ods;
107
108 if ( $html ) {
109     ## carriage return replaced by <br/> as output is html
110     foreach my $message (@all_messages) {
111         local $_ = $message->{'content'};
112         s/\n/<br \/>/g;
113         s/\r//g;
114         $message->{'content'} = $_;
115     }
116
117     $html_filenames = print_notices({
118         messages => \@all_messages,
119         split => $split,
120         output_directory => $output_directory,
121         format => 'html',
122     });
123 }
124
125 if ( @emails ) {
126     my $files = {
127         html => $html_filenames,
128         csv  => $csv_filenames,
129         ods  => $ods_filenames,
130     };
131
132     my $transport = Koha::SMTP::Servers->get_default->transport;
133
134     for my $email ( @emails ) {
135         send_files(
136             {
137                 directory => $output_directory,
138                 files     => $files,
139                 from      => C4::Context->preference('KohaAdminEmailAddress'),    # Should be replaced if bug 8000 is pushed
140                 to        => $email,
141                 transport => $transport,
142             }
143         );
144     }
145 }
146
147 cronlogaction({ action => 'End', info => "COMPLETED" });
148
149 sub print_notices {
150     my ( $params ) = @_;
151
152     my $messages = $params->{messages};
153     my $split = $params->{split};
154     my $output_directory = $params->{output_directory};
155     my $format = $params->{format} // 'html';
156
157     die "Format $format is not known"
158         unless $format =~ m[^html$|^csv$|^ods$];
159
160     my ( @filenames, $messages_by_branch );
161
162     if ( $split ) {
163         foreach my $message (@$messages) {
164             push( @{ $messages_by_branch->{ $message->{'branchcode'} } }, $message );
165         }
166     } else {
167         $messages_by_branch->{all_branches} = $messages;
168     }
169
170     while ( my ( $branchcode, $branch_messages ) = each %$messages_by_branch ) {
171         my $letter_codes = @letter_codes == 0 ? 'all' : join '_', @letter_codes;
172         my $filename = $split
173             ? "notices_$letter_codes-" . $today_iso . "-$branchcode.$format"
174             : "notices_$letter_codes-" . $today_iso . ".$format";
175         my $filepath = File::Spec->catdir( $output_directory, $filename );
176         if ( $format eq 'html' ) {
177             generate_html({
178                 messages => $branch_messages,
179                 filepath => $filepath,
180             });
181         } elsif ( $format eq 'csv' ) {
182             generate_csv ({
183                 messages => $branch_messages,
184                 filepath => $filepath,
185             });
186         } elsif ( $format eq 'ods' ) {
187             _generate_ods ({
188                 messages => $branch_messages,
189                 filepath => $filepath,
190             });
191         }
192
193         if ( $send ) {
194             foreach my $message ( @$branch_messages ) {
195                 C4::Letters::_set_message_status(
196                     {
197                         message_id => $message->{'message_id'},
198                         status => 'sent'
199                     }
200                 );
201             }
202         }
203         push @filenames, $filename;
204     }
205     return \@filenames;
206 }
207
208 sub generate_html {
209     my ( $params ) = @_;
210     my $messages = $params->{messages};
211     my $filepath = $params->{filepath};
212
213     my $template =
214       C4::Templates::gettemplate( 'batch/print-notices.tt', 'intranet',
215         CGI->new );
216
217     foreach my $message (@$messages) {
218         $message->{is_html} = $message->{content_type} && $message->{content_type} =~ /html/i;
219     }
220
221     $template->param(
222         stylesheet => C4::Context->preference("NoticeCSS"),
223         today      => $today_syspref,
224         messages   => $messages,
225     );
226
227     open my $OUTPUT, '>encoding(utf-8)', $filepath
228         or die "Could not open $filepath: $!";
229     print $OUTPUT $template->output;
230     close $OUTPUT;
231 }
232
233 sub generate_csv {
234     my ( $params ) = @_;
235     my $messages = $params->{messages};
236     my $filepath = $params->{filepath};
237
238     open my $OUTPUT, '>encoding(utf-8)', $filepath
239         or die "Could not open $filepath: $!";
240     my $headers;
241     foreach my $message ( @$messages ) {
242         my @lines = split /\n/, $message->{content};
243         chomp for @lines;
244
245         # We don't have headers, get them
246         unless ( $headers ) {
247             $headers = $lines[0];
248             say $OUTPUT $headers;
249         }
250
251         shift @lines;
252         for my $line ( @lines ) {
253             next if $line =~ /^\s$/;
254             say $OUTPUT $line;
255         }
256     }
257 }
258
259 sub _generate_ods {
260     my ( $params ) = @_;
261     my $messages = $params->{messages};
262     my $ods_filepath = $params->{filepath};
263
264     # Prepare sheet
265     my $ods_content;
266     my $has_headers;
267     foreach my $message ( @$messages ) {
268         my @message_lines = split /\n/, $message->{content};
269         chomp for @message_lines;
270         # Get headers from first message
271         if ($has_headers) {
272             shift @message_lines;
273         } else {
274             $has_headers = 1;
275         }
276         foreach my $message_line ( @message_lines ) {
277             my @content_row;
278             my @message_cells = split $delimiter, $message_line;
279             foreach ( @message_cells ) {
280                 push @content_row, Encode::encode( 'UTF8', $_ );
281             }
282             push @$ods_content, \@content_row;
283         }
284     }
285
286     # Process
287     generate_ods($ods_filepath, $ods_content);
288 }
289
290 sub send_files {
291     my ( $params ) = @_;
292     my $directory = $params->{directory};
293     my $files     = $params->{files};
294     my $to        = $params->{to};
295     my $from      = $params->{from};
296     my $transport = $params->{transport};
297
298     return unless $to and $from and $transport;
299
300     my $email = Koha::Email->create(
301         {
302             from    => $from,
303             to      => $to,
304             subject => 'Print notices for ' . $today_syspref,
305         }
306     );
307
308     while ( my ( $type, $filenames ) = each %$files ) {
309         for my $filename ( @$filenames ) {
310             my $mimetype = $type eq 'html'
311                 ? 'text/html'
312                 : $type eq 'csv'
313                     ? 'text/csv'
314                     : $type eq 'ods'
315                         ? 'application/vnd.oasis.opendocument.spreadsheet'
316                         : undef;
317
318             next unless $mimetype;
319
320             my $filepath = File::Spec->catfile( $directory, $filename );
321
322             next unless $filepath or -f $filepath;
323
324             $email->attach_file(
325                 $filepath,
326                 content_type => $mimetype,
327                 charset      => 'UTF-8',
328                 name         => $filename,
329                 disposition  => 'attachment',
330             );
331         }
332     }
333
334     $email->send_or_die( { transport => $transport } );
335
336 }
337
338 =head1 NAME
339
340 gather_print_notices - Print waiting print notices
341
342 =head1 SYNOPSIS
343
344 gather_print_notices output_directory [-s|--split] [--html] [--csv] [--ods] [--letter_code=LETTER_CODE] [-e|--email=your_email@example.org] [-h|--help]
345
346 Will print all waiting print notices to the output_directory.
347
348 The generated filename will be notices-TODAY.[csv|html|ods] or notices-TODAY-BRANCHCODE.[csv|html|ods] if the --split parameter is given.
349
350 =head1 OPTIONS
351
352 =over
353
354 =item B<output_directory>
355
356 Define the output directory where the files will be generated.
357
358 =item B<--send|--nosend>
359
360 After files have been generated, messages status is changed from 'pending' to
361 'sent'. This is the default action, without this parameter or with --send.
362 Using --nosend, the message status is not changed.
363
364 =item B<-s|--split>
365
366 Split messages into separate files by borrower home library to OUTPUT_DIRECTORY/notices-CURRENT_DATE-BRANCHCODE.[csv|html|ods]
367
368 =item B<--html>
369
370 Generate the print notices in a html file (default is --html, if --csv and --ods are not given).
371
372 =item B<--csv>
373
374 Generate the print notices in a csv file.
375 If you use this parameter, the template should contain 2 lines.
376 The first one the csv headers and the second one the value list.
377
378 For example:
379 cardnumber:patron:email:item
380 <<borrowers.cardnumber>>:<<borrowers.firstname>> <<borrowers.surname>>:<<borrowers.email>>:<<items.barcode>>
381
382 You have to combine this option with one (and only one) letter_code.
383
384 =item B<--ods>
385
386 Generate the print notices in a ods file.
387
388 This is the same as the csv parameter but using csv2odf to generate an ods file instead of a csv file.
389
390 =item B<--letter_code>
391
392 Filter print messages by letter_code.
393 Several letter_code parameters can be given.
394
395 =item B<-e|--email>
396
397 Repeatable.
398 E-mail address to send generated files to.
399
400 =item B<-h|--help>
401
402 Print a brief help message
403
404 =back
405
406 =head1 AUTHOR
407
408 Jesse Weaver <pianohacker@gmail.com>
409
410 Jonathan Druart <jonathan.druart@biblibre.com>
411
412 =head1 COPYRIGHT
413
414 Copyright 2009 Jesse Weaver
415
416 Copyright 2014 BibLibre
417
418 =head1 LICENSE
419 This file is part of Koha.
420
421 Koha is free software; you can redistribute it and/or modify it
422 under the terms of the GNU General Public License as published by
423 the Free Software Foundation; either version 3 of the License, or
424 (at your option) any later version.
425
426 Koha is distributed in the hope that it will be useful, but
427 WITHOUT ANY WARRANTY; without even the implied warranty of
428 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
429 GNU General Public License for more details.
430
431 You should have received a copy of the GNU General Public License
432 along with Koha; if not, see <http://www.gnu.org/licenses>.
433
434 =cut