Bug 31373: DRY - Use try/catch around _process_tt
[koha.git] / tools / batchMod.pl
1 #!/usr/bin/perl
2
3
4 # Copyright 2000-2002 Katipo Communications
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21 use CGI qw ( -utf8 );
22 use Modern::Perl;
23 use Try::Tiny qw( catch try );
24
25 use C4::Auth qw( get_template_and_user haspermission );
26 use C4::Output qw( output_html_with_http_headers );
27 use C4::Circulation qw( barcodedecode );
28 use C4::Context;
29 use MARC::File::XML;
30 use List::MoreUtils qw( uniq );
31 use Encode qw( encode_utf8 );
32
33 use Koha::Database;
34 use Koha::Exception;
35 use Koha::Biblios;
36 use Koha::Items;
37 use Koha::Patrons;
38 use Koha::Item::Attributes;
39 use Koha::BackgroundJob::BatchDeleteItem;
40 use Koha::BackgroundJob::BatchUpdateItem;
41 use Koha::UI::Form::Builder::Item;
42 use Koha::UI::Table::Builder::Items;
43
44 my $input = CGI->new;
45 my $dbh = C4::Context->dbh;
46 my $error        = $input->param('error');
47 my @itemnumbers  = $input->multi_param('itemnumber');
48 my $biblionumber = $input->param('biblionumber');
49 my $op           = $input->param('op');
50 my $del          = $input->param('del');
51 my $del_records  = $input->param('del_records');
52 my $src          = $input->param('src');
53 my $use_default_values = $input->param('use_default_values');
54 my $exclude_from_local_holds_priority = $input->param('exclude_from_local_holds_priority');
55
56 my $template_name;
57 my $template_flag;
58 if (!defined $op) {
59     $template_name = "tools/batchMod.tt";
60     $template_flag = { tools => '*' };
61     $op = q{};
62 } else {
63     $template_name = ($del) ? "tools/batchMod-del.tt" : "tools/batchMod-edit.tt";
64     $template_flag = ($del) ? { tools => 'items_batchdel' }   : { tools => 'items_batchmod' };
65 }
66
67 my ($template, $loggedinuser, $cookie)
68     = get_template_and_user({template_name => $template_name,
69                  query => $input,
70                  type => "intranet",
71                  flagsrequired => $template_flag,
72                  });
73
74 $template->param( searchid => scalar $input->param('searchid'), );
75
76 # Does the user have a restricted item edition permission?
77 my $uid = $loggedinuser ? Koha::Patrons->find( $loggedinuser )->userid : undef;
78 my $restrictededition = $uid ? haspermission($uid,  {'tools' => 'items_batchmod_restricted'}) : undef;
79 # In case user is a superlibrarian, edition is not restricted
80 $restrictededition = 0 if ($restrictededition != 0 && C4::Context->IsSuperLibrarian());
81
82 my $nextop="";
83 my $display_items;
84
85 my @messages;
86
87 if ( $op eq "action" ) {
88
89     if ($del) {
90         try {
91             my $params = {
92                 record_ids     => \@itemnumbers,
93                 delete_biblios => $del_records,
94             };
95             my $job_id =
96               Koha::BackgroundJob::BatchDeleteItem->new->enqueue($params);
97             $nextop = 'enqueued';
98             $template->param( job_id => $job_id, );
99         }
100         catch {
101             warn $_;
102             push @messages,
103               {
104                 type  => 'error',
105                 code  => 'cannot_enqueue_job',
106                 error => $_,
107               };
108             $template->param( view => 'errors' );
109         };
110     }
111
112     else {    # modification
113
114         my @item_columns = Koha::Items->columns;
115
116         my $new_item_data;
117         my ( $columns_with_regex );
118         my @subfields_to_blank = $input->multi_param('disable_input');
119         my @more_subfields = $input->multi_param("items.more_subfields_xml");
120         for my $item_column (@item_columns) {
121             my @attributes       = ($item_column);
122             my $cgi_param_prefix = 'items.';
123             if ( $item_column eq 'more_subfields_xml' ) {
124                 @attributes       = ();
125                 $cgi_param_prefix = 'items.more_subfields_xml_';
126                 for my $subfield (@more_subfields) {
127                     push @attributes, $subfield;
128                 }
129             }
130
131             for my $attr (@attributes) {
132
133                 my $cgi_var_name = $cgi_param_prefix
134                   . encode_utf8($attr)
135                   ;  # We need to deal correctly with encoding on subfield codes
136
137                 if ( grep { $cgi_var_name eq $_ } @subfields_to_blank ) {
138                     # Empty this column
139                     $new_item_data->{$attr} = undef;
140                 }
141                 elsif ( my $regex_search =
142                     $input->param( $cgi_var_name . '_regex_search' ) )
143                 {
144                     $columns_with_regex->{$attr} = {
145                         search => $regex_search,
146                         replace =>
147                           $input->param( $cgi_var_name . '_regex_replace' ),
148                         modifiers =>
149                           $input->param( $cgi_var_name . '_regex_modifiers' )
150                     };
151                 }
152                 else {
153                     my @v =
154                       grep { $_ ne "" } uniq $input->multi_param($cgi_var_name);
155
156                     next unless @v;
157
158                     $new_item_data->{$attr} = join '|', @v;
159                 }
160             }
161         }
162
163         my $params = {
164             record_ids                        => \@itemnumbers,
165             regex_mod                         => $columns_with_regex,
166             new_values                        => $new_item_data,
167             exclude_from_local_holds_priority => (
168                 defined $exclude_from_local_holds_priority
169                   && $exclude_from_local_holds_priority ne ""
170               )
171             ? $exclude_from_local_holds_priority
172             : undef,
173
174         };
175         try {
176             my $job_id =
177               Koha::BackgroundJob::BatchUpdateItem->new->enqueue($params);
178             $nextop = 'enqueued';
179             $template->param( job_id => $job_id, );
180         }
181         catch {
182             push @messages,
183               {
184                 type  => 'error',
185                 code  => 'cannot_enqueue_job',
186                 error => $_,
187               };
188             $template->param( view => 'errors' );
189         };
190     }
191
192 }
193
194 $template->param(
195     messages => \@messages,
196 );
197 #
198 #-------------------------------------------------------------------------------
199 # build screen with existing items. and "new" one
200 #-------------------------------------------------------------------------------
201
202 if ($op eq "show"){
203     my $filefh = $input->upload('uploadfile');
204     my $filecontent = $input->param('filecontent');
205     my ( @notfoundbarcodes, @notfounditemnumbers);
206
207     my $split_chars = C4::Context->preference('BarcodeSeparators');
208     if ($filefh){
209         binmode $filefh, ':encoding(UTF-8)';
210         my @contentlist;
211         while (my $content=<$filefh>){
212             $content =~ s/[\r\n]*$//;
213             push @contentlist, $content if $content;
214         }
215
216         if ($filecontent eq 'barcode_file') {
217             @contentlist = grep /\S/, ( map { split /[$split_chars]/ } @contentlist );
218             @contentlist = uniq @contentlist;
219             # Note: adding lc for case insensitivity
220             my %itemdata = map { lc($_->{barcode}) => $_->{itemnumber} } @{ Koha::Items->search({ barcode => { -in => \@contentlist } }, { columns => [ 'itemnumber', 'barcode' ] } )->unblessed };
221             @itemnumbers = map { exists $itemdata{lc $_} ? $itemdata{lc $_} : () } @contentlist;
222             @notfoundbarcodes = grep { !exists $itemdata{lc $_} } @contentlist;
223         }
224         elsif ( $filecontent eq 'itemid_file') {
225             @contentlist = uniq @contentlist;
226             my %itemdata = map { $_->{itemnumber} => 1 } @{ Koha::Items->search({ itemnumber => { -in => \@contentlist } }, { columns => [ 'itemnumber' ] } )->unblessed };
227             @itemnumbers = grep { exists $itemdata{$_} } @contentlist;
228             @notfounditemnumbers = grep { !exists $itemdata{$_} } @contentlist;
229         }
230     } else {
231         if (defined $biblionumber && !@itemnumbers){
232             my $biblio = Koha::Biblios->find($biblionumber);
233             @itemnumbers = $biblio ? $biblio->items->get_column('itemnumber') : ();
234         }
235         if ( my $list = $input->param('barcodelist') ) {
236             my @barcodelist = grep /\S/, ( split /[$split_chars]/, $list );
237             @barcodelist = uniq @barcodelist;
238
239             @barcodelist = map { barcodedecode( $_ ) } @barcodelist;
240
241             # Note: adding lc for case insensitivity
242             my %itemdata = map { lc($_->{barcode}) => $_->{itemnumber} } @{ Koha::Items->search({ barcode => { -in => \@barcodelist } }, { columns => [ 'itemnumber', 'barcode' ] } )->unblessed };
243             @itemnumbers = map { exists $itemdata{lc $_} ? $itemdata{lc $_} : () } @barcodelist;
244             @notfoundbarcodes = grep { !exists $itemdata{lc $_} } @barcodelist;
245         }
246     }
247
248     # Flag to tell the template there are valid results, hidden or not
249     if(scalar(@itemnumbers) > 0){ $template->param("itemresults" => 1); }
250     # Only display the items if there are no more than pref MaxItemsToProcessForBatchMod or MaxItemsToDisplayForBatchDel
251     my $max_display_items = $del
252         ? C4::Context->preference("MaxItemsToDisplayForBatchDel")
253         : C4::Context->preference("MaxItemsToDisplayForBatchMod");
254     $template->param("too_many_items_process" => scalar(@itemnumbers)) if !$del && scalar(@itemnumbers) > C4::Context->preference("MaxItemsToProcessForBatchMod");
255     if (scalar(@itemnumbers) <= ( $max_display_items // 1000 ) ) {
256         $display_items = 1;
257     } else {
258         $template->param("too_many_items_display" => scalar(@itemnumbers));
259         # Even if we do not display the items, we need the itemnumbers
260         $template->param(itemnumbers_array => \@itemnumbers);
261     }
262
263     # now, build the item form for entering a new item
264
265     # Getting list of subfields to keep when restricted batchmod edit is enabled
266     my @subfields_to_allow = $restrictededition ? split ' ', C4::Context->preference('SubfieldsToAllowForRestrictedBatchmod') : ();
267
268     my $subfields = Koha::UI::Form::Builder::Item->new->edit_form( # NOTE: We are not passing a biblionumber intentionally !
269         {
270             restricted_editition => $restrictededition,
271             (
272                 @subfields_to_allow
273                 ? ( subfields_to_allow => \@subfields_to_allow )
274                 : ()
275             ),
276             ignore_not_allowed_subfields => 1,
277             kohafields_to_ignore         => ['items.barcode'],
278             prefill_with_default_values => $use_default_values,
279             default_branches_empty      => 1,
280         }
281     );
282
283     # what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
284     $template->param(
285         subfields           => $subfields,
286         notfoundbarcodes    => \@notfoundbarcodes,
287         notfounditemnumbers => \@notfounditemnumbers
288     );
289     $nextop="action"
290 } # -- End action="show"
291
292 if ( $display_items ) {
293     my $items_table =
294       Koha::UI::Table::Builder::Items->new( { itemnumbers => \@itemnumbers } )
295       ->build_table;
296     $template->param(
297         items        => $items_table->{items},
298         item_header_loop => $items_table->{headers},
299     );
300 }
301
302 $template->param(
303     op  => $nextop,
304     del => $del,
305     ( $op ? ( $op => 1 ) : () ),
306     src          => $src,
307     biblionumber => $biblionumber,
308 );
309
310 output_html_with_http_headers $input, $cookie, $template->output;