Bug 10694: (follow-up) fix various issues
[koha.git] / tools / letter.pl
1 #!/usr/bin/perl
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 =head1 tools/letter.pl
21
22  ALGO :
23  this script use an $op to know what to do.
24  if $op is empty or none of the values listed below,
25         - the default screen is built (with all or filtered (if search string is set) records).
26         - the   user can click on add, modify or delete record.
27     - filtering is done on the code field
28  if $op=add_form
29         - if primary key (module + code) exists, this is a modification,so we read the required record
30         - builds the add/modify form
31  if $op=add_validate
32         - the user has just send data, so we create/modify the record
33  if $op=delete_form
34         - we show the record selected and ask for confirmation
35  if $op=delete_confirm
36         - we delete the designated record
37
38 =cut
39
40 # TODO This script drives the CRUD operations on the letter table
41 # The DB interaction should be handled by calls to C4/Letters.pm
42
43 use strict;
44 use warnings;
45 use CGI;
46 use C4::Auth;
47 use C4::Context;
48 use C4::Output;
49 use C4::Branch; # GetBranches
50 use C4::Letters;
51 use C4::Members::Attributes;
52
53 # _letter_from_where($branchcode,$module, $code, $mtt)
54 # - return FROM WHERE clause and bind args for a letter
55 sub _letter_from_where {
56     my ($branchcode, $module, $code, $mtt) = @_;
57     my $sql = q{FROM letter WHERE branchcode = ? AND module = ? AND code = ?};
58     $sql .= q{ AND message_transport_type = ?} if $mtt ne '*';
59     my @args = ( $branchcode || '', $module, $code, ($mtt ne '*' ? $mtt : ()) );
60 # Mysql is retarded. cause branchcode is part of the primary key it cannot be null. How does that
61 # work with foreign key constraint I wonder...
62
63 #   if ($branchcode) {
64 #       $sql .= " AND branchcode = ?";
65 #       push @args, $branchcode;
66 #   } else {
67 #       $sql .= " AND branchcode IS NULL";
68 #   }
69
70     return ($sql, \@args);
71 }
72
73 # get_letters($branchcode,$module, $code, $mtt)
74 # - return letters with the given $branchcode, $module, $code and $mtt exists
75 sub get_letters {
76     my ($sql, $args) = _letter_from_where(@_);
77     my $dbh = C4::Context->dbh;
78     my $letter = $dbh->selectall_hashref("SELECT * $sql", 'message_transport_type', undef, @$args);
79     return $letter;
80 }
81
82 # $protected_letters = protected_letters()
83 # - return a hashref of letter_codes representing letters that should never be deleted
84 sub protected_letters {
85     my $dbh = C4::Context->dbh;
86     my $codes = $dbh->selectall_arrayref(q{SELECT DISTINCT letter_code FROM message_transports});
87     return { map { $_->[0] => 1 } @{$codes} };
88 }
89
90 our $input       = new CGI;
91 my $searchfield = $input->param('searchfield');
92 my $script_name = '/cgi-bin/koha/tools/letter.pl';
93 our $branchcode  = $input->param('branchcode');
94 my $code        = $input->param('code');
95 my $module      = $input->param('module') || '';
96 my $content     = $input->param('content');
97 my $op          = $input->param('op') || '';
98 my $dbh = C4::Context->dbh;
99
100 our ( $template, $borrowernumber, $cookie, $staffflags ) = get_template_and_user(
101     {
102         template_name   => 'tools/letter.tmpl',
103         query           => $input,
104         type            => 'intranet',
105         authnotrequired => 0,
106         flagsrequired   => { tools => 'edit_notices' },
107         debug           => 1,
108     }
109 );
110
111 our $my_branch = C4::Context->preference("IndependentBranches") && !$staffflags->{'superlibrarian'}
112   ?  C4::Context->userenv()->{'branch'}
113   : undef;
114 # we show only the TMPL_VAR names $op
115
116 $template->param(
117     independant_branch => $my_branch,
118         script_name => $script_name,
119   searchfield => $searchfield,
120     branchcode => $branchcode,
121         action => $script_name
122 );
123
124 if ($op eq 'copy') {
125     add_copy();
126     $op = 'add_form';
127 }
128
129 if ($op eq 'add_form') {
130     add_form($branchcode, $module, $code);
131 }
132 elsif ( $op eq 'add_validate' ) {
133     add_validate();
134     $op = q{}; # next operation is to return to default screen
135 }
136 elsif ( $op eq 'delete_confirm' ) {
137     delete_confirm($branchcode, $module, $code);
138 }
139 elsif ( $op eq 'delete_confirmed' ) {
140     my $mtt = $input->param('message_transport_type');
141     delete_confirmed($branchcode, $module, $code, $mtt);
142     $op = q{}; # next operation is to return to default screen
143 }
144 else {
145     default_display($branchcode,$searchfield);
146 }
147
148 # Do this last as delete_confirmed resets
149 if ($op) {
150     $template->param($op  => 1);
151 } else {
152     $template->param(no_op_set => 1);
153 }
154
155 output_html_with_http_headers $input, $cookie, $template->output;
156
157 sub add_form {
158     my ( $branchcode,$module, $code ) = @_;
159
160     my $letters;
161     # if code has been passed we can identify letter and its an update action
162     if ($code) {
163         $letters = get_letters($branchcode,$module, $code, '*');
164     }
165
166     my $message_transport_types = GetMessageTransportTypes();
167     my @letter_loop;
168     if ($letters) {
169         $template->param(
170             modify     => 1,
171             code       => $code,
172             branchcode => $branchcode,
173         );
174         my $first_flag = 1;
175         # The letter name is contained into each mtt row.
176         # So we can only sent the first one to the template.
177         for my $mtt ( @$message_transport_types ) {
178             # The letter_name
179             if ( $first_flag and $letters->{$mtt}{name} ) {
180                 $template->param(
181                     letter_name=> $letters->{$mtt}{name},
182                 );
183                 $first_flag = 0;
184             }
185
186             push @letter_loop, {
187                 message_transport_type => $mtt,
188                 is_html    => $letters->{$mtt}{is_html},
189                 title      => $letters->{$mtt}{title},
190                 content    => $letters->{$mtt}{content}//'',
191             };
192         }
193     }
194     else { # initialize the new fields
195         for my $mtt ( @$message_transport_types ) {
196             push @letter_loop, {
197                 message_transport_type => $mtt,
198             }
199         }
200         $template->param(
201             branchcode => $branchcode,
202             module     => $module,
203         );
204         $template->param( adding => 1 );
205     }
206
207     $template->param(
208         letters => \@letter_loop,
209     );
210
211     my $field_selection;
212     push @{$field_selection}, add_fields('branches');
213     if ($module eq 'reserves') {
214         push @{$field_selection}, add_fields('borrowers', 'reserves', 'biblio', 'items');
215     }
216     elsif ($module eq 'claimacquisition') {
217         push @{$field_selection}, add_fields('aqbooksellers', 'aqorders', 'biblio', 'biblioitems');
218     }
219     elsif ($module eq 'claimissues') {
220         push @{$field_selection}, add_fields('aqbooksellers', 'serial', 'subscription');
221         push @{$field_selection},
222         {
223             value => q{},
224             text => '---BIBLIO---'
225         };
226         foreach(qw(title author serial)) {
227             push @{$field_selection}, {value => "biblio.$_", text => ucfirst $_ };
228         }
229     }
230     elsif ($module eq 'suggestions') {
231         push @{$field_selection}, add_fields('suggestions', 'borrowers', 'biblio');
232     }
233     else {
234         push @{$field_selection}, add_fields('biblio','biblioitems'),
235             add_fields('items'),
236             {value => 'items.content', text => 'items.content'},
237             {value => 'items.fine',    text => 'items.fine'},
238             add_fields('borrowers');
239         if ($module eq 'circulation') {
240             push @{$field_selection}, add_fields('opac_news');
241
242         }
243
244         if ( $module eq 'circulation' && $code eq "CHECKIN" ) {
245             push @{$field_selection}, add_fields('old_issues');
246         } else {
247             push @{$field_selection}, add_fields('issues');
248         }
249     }
250
251     $template->param(
252         module     => $module,
253         branchloop => _branchloop($branchcode),
254         SQLfieldname => $field_selection,
255     );
256     return;
257 }
258
259 sub add_validate {
260     my $dbh        = C4::Context->dbh;
261     my $oldbranchcode = $input->param('oldbranchcode');
262     my $branchcode    = $input->param('branchcode') || '';
263     my $module        = $input->param('module');
264     my $oldmodule     = $input->param('oldmodule');
265     my $code          = $input->param('code');
266     my $name          = $input->param('name');
267     my @mtt           = $input->param('message_transport_type');
268     my @title         = $input->param('title');
269     my @content       = $input->param('content');
270     for my $mtt ( @mtt ) {
271         my $is_html = $input->param("is_html_$mtt");
272         my $title   = shift @title;
273         my $content = shift @content;
274         my $letter = get_letters($oldbranchcode,$oldmodule, $code, $mtt);
275         unless ( $title and $content ) {
276             delete_confirmed( $oldbranchcode, $oldmodule, $code, $mtt );
277             next;
278         }
279         if ( exists $letter->{$mtt} ) {
280             $dbh->do(
281                 q{
282                     UPDATE letter
283                     SET branchcode = ?, module = ?, name = ?, is_html = ?, title = ?, content = ?
284                     WHERE branchcode = ? AND module = ? AND code = ? AND message_transport_type = ?
285                 },
286                 undef,
287                 $branchcode, $module, $name, $is_html || 0, $title, $content,
288                 $oldbranchcode, $oldmodule, $code, $mtt
289             );
290         } else {
291             $dbh->do(
292                 q{INSERT INTO letter (branchcode,module,code,name,is_html,title,content,message_transport_type) VALUES (?,?,?,?,?,?,?,?)},
293                 undef,
294                 $branchcode, $module, $code, $name, $is_html || 0, $title, $content, $mtt
295             );
296         }
297     }
298     # set up default display
299     default_display($branchcode);
300 }
301
302 sub add_copy {
303     my $dbh        = C4::Context->dbh;
304     my $oldbranchcode = $input->param('oldbranchcode');
305     my $branchcode    = $input->param('branchcode');
306     my $module        = $input->param('module');
307     my $code          = $input->param('code');
308
309     return if keys %{ get_letters($branchcode,$module, $code, '*') };
310
311     my $old_letters = get_letters($oldbranchcode,$module, $code, '*');
312
313     my $message_transport_types = GetMessageTransportTypes();
314     for my $mtt ( @$message_transport_types ) {
315         next unless exists $old_letters->{$mtt};
316         my $old_letter = $old_letters->{$mtt};
317
318         $dbh->do(
319             q{INSERT INTO letter (branchcode,module,code,name,is_html,title,content,message_transport_type) VALUES (?,?,?,?,?,?,?,?)},
320             undef,
321             $branchcode, $module, $code, $old_letter->{name}, $old_letter->{is_html}, $old_letter->{title}, $old_letter->{content}, $mtt
322         );
323     }
324 }
325
326 sub delete_confirm {
327     my ($branchcode, $module, $code) = @_;
328     my $dbh = C4::Context->dbh;
329     my $letter = get_letters($branchcode, $module, $code, '*');
330     my @values = values %$letter;
331     $template->param(
332         branchcode => $branchcode,
333         branchname => GetBranchName($branchcode),
334         code => $code,
335         module => $module,
336         name => $values[0]->{name},
337     );
338     return;
339 }
340
341 sub delete_confirmed {
342     my ($branchcode, $module, $code, $mtt) = @_;
343     my ($sql, $args) = _letter_from_where($branchcode, $module, $code, $mtt);
344     my $dbh    = C4::Context->dbh;
345     $dbh->do("DELETE $sql", undef, @$args);
346     # setup default display for screen
347     default_display($branchcode);
348     return;
349 }
350
351 sub retrieve_letters {
352     my ($branchcode, $searchstring) = @_;
353
354     $branchcode = $my_branch if $branchcode && $my_branch;
355
356     my $dbh = C4::Context->dbh;
357     my ($sql, @where, @args);
358     $sql = "SELECT branchcode, module, code, name, branchname
359             FROM letter
360             LEFT OUTER JOIN branches USING (branchcode)
361     ";
362     if ($searchstring && $searchstring=~m/(\S+)/) {
363         $searchstring = $1 . q{%};
364         push @where, 'code LIKE ?';
365         push @args, $searchstring;
366     }
367     elsif ($branchcode) {
368         push @where, 'branchcode = ?';
369         push @args, $branchcode || '';
370     }
371     elsif ($my_branch) {
372         push @where, "(branchcode = ? OR branchcode = '')";
373         push @args, $my_branch;
374     }
375
376     $sql .= " WHERE ".join(" AND ", @where) if @where;
377     $sql .= " GROUP BY branchcode,module,code";
378     $sql .= " ORDER BY module, code, branchcode";
379
380     return $dbh->selectall_arrayref($sql, { Slice => {} }, @args);
381 }
382
383 sub default_display {
384     my ($branchcode, $searchfield) = @_;
385
386     if ( $searchfield  ) {
387         $template->param( search      => 1 );
388     }
389     my $results = retrieve_letters($branchcode,$searchfield);
390
391     my $loop_data = [];
392     my $protected_letters = protected_letters();
393     foreach my $row (@{$results}) {
394         $row->{protected} = !$row->{branchcode} && $protected_letters->{ $row->{code} };
395         push @{$loop_data}, $row;
396
397     }
398
399     $template->param(
400         letter => $loop_data,
401         branchloop => _branchloop($branchcode),
402     );
403 }
404
405 sub _branchloop {
406     my ($branchcode) = @_;
407
408     my $branches = GetBranches();
409     my @branchloop;
410     for my $thisbranch (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } keys %$branches) {
411         push @branchloop, {
412             value      => $thisbranch,
413             selected   => $branchcode && $thisbranch eq $branchcode,
414             branchname => $branches->{$thisbranch}->{'branchname'},
415         };
416     }
417
418     return \@branchloop;
419 }
420
421 sub add_fields {
422     my @tables = @_;
423     my @fields = ();
424
425     for my $table (@tables) {
426         push @fields, get_columns_for($table);
427
428     }
429     return @fields;
430 }
431
432 sub get_columns_for {
433     my $table = shift;
434 # FIXME untranslateable
435     my %column_map = (
436         aqbooksellers => '---BOOKSELLERS---',
437         aqorders      => '---ORDERS---',
438         serial        => '---SERIALS---',
439         reserves      => '---HOLDS---',
440         suggestions   => '---SUGGESTIONS---',
441     );
442     my @fields = ();
443     if (exists $column_map{$table} ) {
444         push @fields, {
445             value => q{},
446             text  => $column_map{$table} ,
447         };
448     }
449     else {
450         my $tlabel = '---' . uc $table;
451         $tlabel.= '---';
452         push @fields, {
453             value => q{},
454             text  => $tlabel,
455         };
456     }
457
458     my $sql = "SHOW COLUMNS FROM $table";# TODO not db agnostic
459     my $table_prefix = $table . q|.|;
460     my $rows = C4::Context->dbh->selectall_arrayref($sql, { Slice => {} });
461     for my $row (@{$rows}) {
462         next if $row->{'Field'} eq 'timestamp'; # this is really an irrelevant field and there may be other common fields that should be excluded from the list
463         push @fields, {
464             value => $table_prefix . $row->{Field},
465             text  => $table_prefix . $row->{Field},
466         }
467     }
468     if ($table eq 'borrowers') {
469         if ( my $attributes = C4::Members::Attributes::GetAttributes() ) {
470             foreach (@$attributes) {
471                 push @fields, {
472                     value => "borrower-attribute:$_",
473                     text  => "attribute:$_",
474                 }
475             }
476         }
477     }
478     return @fields;
479 }