Bug 32401: Remove x-koha-query support
[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     $template->param(
218         stylesheet => C4::Context->preference("NoticeCSS"),
219         today      => $today_syspref,
220         messages   => $messages,
221     );
222
223     open my $OUTPUT, '>encoding(utf-8)', $filepath
224         or die "Could not open $filepath: $!";
225     print $OUTPUT $template->output;
226     close $OUTPUT;
227 }
228
229 sub generate_csv {
230     my ( $params ) = @_;
231     my $messages = $params->{messages};
232     my $filepath = $params->{filepath};
233
234     open my $OUTPUT, '>encoding(utf-8)', $filepath
235         or die "Could not open $filepath: $!";
236     my $headers;
237     foreach my $message ( @$messages ) {
238         my @lines = split /\n/, $message->{content};
239         chomp for @lines;
240
241         # We don't have headers, get them
242         unless ( $headers ) {
243             $headers = $lines[0];
244             say $OUTPUT $headers;
245         }
246
247         shift @lines;
248         for my $line ( @lines ) {
249             next if $line =~ /^\s$/;
250             say $OUTPUT $line;
251         }
252     }
253 }
254
255 sub _generate_ods {
256     my ( $params ) = @_;
257     my $messages = $params->{messages};
258     my $ods_filepath = $params->{filepath};
259
260     # Prepare sheet
261     my $ods_content;
262     my $has_headers;
263     foreach my $message ( @$messages ) {
264         my @message_lines = split /\n/, $message->{content};
265         chomp for @message_lines;
266         # Get headers from first message
267         if ($has_headers) {
268             shift @message_lines;
269         } else {
270             $has_headers = 1;
271         }
272         foreach my $message_line ( @message_lines ) {
273             my @content_row;
274             my @message_cells = split $delimiter, $message_line;
275             foreach ( @message_cells ) {
276                 push @content_row, Encode::encode( 'UTF8', $_ );
277             }
278             push @$ods_content, \@content_row;
279         }
280     }
281
282     # Process
283     generate_ods($ods_filepath, $ods_content);
284 }
285
286 sub send_files {
287     my ( $params ) = @_;
288     my $directory = $params->{directory};
289     my $files     = $params->{files};
290     my $to        = $params->{to};
291     my $from      = $params->{from};
292     my $transport = $params->{transport};
293
294     return unless $to and $from and $transport;
295
296     my $email = Koha::Email->create(
297         {
298             from    => $from,
299             to      => $to,
300             subject => 'Print notices for ' . $today_syspref,
301         }
302     );
303
304     while ( my ( $type, $filenames ) = each %$files ) {
305         for my $filename ( @$filenames ) {
306             my $mimetype = $type eq 'html'
307                 ? 'text/html'
308                 : $type eq 'csv'
309                     ? 'text/csv'
310                     : $type eq 'ods'
311                         ? 'application/vnd.oasis.opendocument.spreadsheet'
312                         : undef;
313
314             next unless $mimetype;
315
316             my $filepath = File::Spec->catfile( $directory, $filename );
317
318             next unless $filepath or -f $filepath;
319
320             $email->attach_file(
321                 $filepath,
322                 content_type => $mimetype,
323                 name         => $filename,
324                 disposition  => 'attachment',
325             );
326         }
327     }
328
329     $email->send_or_die( { transport => $transport } );
330
331 }
332
333 =head1 NAME
334
335 gather_print_notices - Print waiting print notices
336
337 =head1 SYNOPSIS
338
339 gather_print_notices output_directory [-s|--split] [--html] [--csv] [--ods] [--letter_code=LETTER_CODE] [-e|--email=your_email@example.org] [-h|--help]
340
341 Will print all waiting print notices to the output_directory.
342
343 The generated filename will be notices-TODAY.[csv|html|ods] or notices-TODAY-BRANCHCODE.[csv|html|ods] if the --split parameter is given.
344
345 =head1 OPTIONS
346
347 =over
348
349 =item B<output_directory>
350
351 Define the output directory where the files will be generated.
352
353 =item B<--send|--nosend>
354
355 After files have been generated, messages status is changed from 'pending' to
356 'sent'. This is the default action, without this parameter or with --send.
357 Using --nosend, the message status is not changed.
358
359 =item B<-s|--split>
360
361 Split messages into separate files by borrower home library to OUTPUT_DIRECTORY/notices-CURRENT_DATE-BRANCHCODE.[csv|html|ods]
362
363 =item B<--html>
364
365 Generate the print notices in a html file (default is --html, if --csv and --ods are not given).
366
367 =item B<--csv>
368
369 Generate the print notices in a csv file.
370 If you use this parameter, the template should contain 2 lines.
371 The first one the csv headers and the second one the value list.
372
373 For example:
374 cardnumber:patron:email:item
375 <<borrowers.cardnumber>>:<<borrowers.firstname>> <<borrowers.surname>>:<<borrowers.email>>:<<items.barcode>>
376
377 You have to combine this option with one (and only one) letter_code.
378
379 =item B<--ods>
380
381 Generate the print notices in a ods file.
382
383 This is the same as the csv parameter but using csv2odf to generate an ods file instead of a csv file.
384
385 =item B<--letter_code>
386
387 Filter print messages by letter_code.
388 Several letter_code parameters can be given.
389
390 =item B<-e|--email>
391
392 Repeatable.
393 E-mail address to send generated files to.
394
395 =item B<-h|--help>
396
397 Print a brief help message
398
399 =back
400
401 =head1 AUTHOR
402
403 Jesse Weaver <pianohacker@gmail.com>
404
405 Jonathan Druart <jonathan.druart@biblibre.com>
406
407 =head1 COPYRIGHT
408
409 Copyright 2009 Jesse Weaver
410
411 Copyright 2014 BibLibre
412
413 =head1 LICENSE
414 This file is part of Koha.
415
416 Koha is free software; you can redistribute it and/or modify it
417 under the terms of the GNU General Public License as published by
418 the Free Software Foundation; either version 3 of the License, or
419 (at your option) any later version.
420
421 Koha is distributed in the hope that it will be useful, but
422 WITHOUT ANY WARRANTY; without even the implied warranty of
423 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
424 GNU General Public License for more details.
425
426 You should have received a copy of the GNU General Public License
427 along with Koha; if not, see <http://www.gnu.org/licenses>.
428
429 =cut