Bug 28445: Use the task queue for the batch delete and update items tool
[srvgit] / 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::DateUtils qw( dt_from_string );
35 use Koha::Exceptions::Exception;
36 use Koha::Biblios;
37 use Koha::Items;
38 use Koha::Patrons;
39 use Koha::Item::Attributes;
40 use Koha::BackgroundJob::BatchDeleteItem;
41 use Koha::BackgroundJob::BatchUpdateItem;
42 use Koha::UI::Form::Builder::Item;
43 use Koha::UI::Table::Builder::Items;
44
45 my $input = CGI->new;
46 my $dbh = C4::Context->dbh;
47 my $error        = $input->param('error');
48 my @itemnumbers  = $input->multi_param('itemnumber');
49 my $biblionumber = $input->param('biblionumber');
50 my $op           = $input->param('op');
51 my $del          = $input->param('del');
52 my $del_records  = $input->param('del_records');
53 my $src          = $input->param('src');
54 my $use_default_values = $input->param('use_default_values');
55 my $exclude_from_local_holds_priority = $input->param('exclude_from_local_holds_priority');
56
57 my $template_name;
58 my $template_flag;
59 if (!defined $op) {
60     $template_name = "tools/batchMod.tt";
61     $template_flag = { tools => '*' };
62     $op = q{};
63 } else {
64     $template_name = ($del) ? "tools/batchMod-del.tt" : "tools/batchMod-edit.tt";
65     $template_flag = ($del) ? { tools => 'items_batchdel' }   : { tools => 'items_batchmod' };
66 }
67
68 my ($template, $loggedinuser, $cookie)
69     = get_template_and_user({template_name => $template_name,
70                  query => $input,
71                  type => "intranet",
72                  flagsrequired => $template_flag,
73                  });
74
75 $template->param( searchid => scalar $input->param('searchid'), );
76
77 # Does the user have a restricted item edition permission?
78 my $uid = $loggedinuser ? Koha::Patrons->find( $loggedinuser )->userid : undef;
79 my $restrictededition = $uid ? haspermission($uid,  {'tools' => 'items_batchmod_restricted'}) : undef;
80 # In case user is a superlibrarian, edition is not restricted
81 $restrictededition = 0 if ($restrictededition != 0 && C4::Context->IsSuperLibrarian());
82
83 my $nextop="";
84 my $display_items;
85
86 my %cookies = parse CGI::Cookie($cookie);
87 my $sessionID = $cookies{'CGISESSID'}->value;
88
89 my @messages;
90
91 if ( $op eq "action" ) {
92
93     if ($del) {
94         try {
95             my $params = {
96                 record_ids     => \@itemnumbers,
97                 delete_biblios => $del_records,
98             };
99             my $job_id =
100               Koha::BackgroundJob::BatchDeleteItem->new->enqueue($params);
101             $nextop = 'enqueued';
102             $template->param( job_id => $job_id, );
103         }
104         catch {
105             warn $_;
106             push @messages,
107               {
108                 type  => 'error',
109                 code  => 'cannot_enqueue_job',
110                 error => $_,
111               };
112             $template->param( view => 'errors' );
113         };
114     }
115
116     else {    # modification
117
118         my @item_columns = Koha::Items->columns;
119
120         my $new_item_data;
121         my ( $columns_with_regex );
122         my @subfields_to_blank = $input->multi_param('disable_input');
123         my @more_subfields = $input->multi_param("items.more_subfields_xml");
124         for my $item_column (@item_columns) {
125             my @attributes       = ($item_column);
126             my $cgi_param_prefix = 'items.';
127             if ( $item_column eq 'more_subfields_xml' ) {
128                 @attributes       = ();
129                 $cgi_param_prefix = 'items.more_subfields_xml_';
130                 for my $subfield (@more_subfields) {
131                     push @attributes, $subfield;
132                 }
133             }
134
135             for my $attr (@attributes) {
136
137                 my $cgi_var_name = $cgi_param_prefix
138                   . encode_utf8($attr)
139                   ;  # We need to deal correctly with encoding on subfield codes
140
141                 if ( grep { $attr eq $_ } @subfields_to_blank ) {
142
143                     # Empty this column
144                     $new_item_data->{$attr} = undef;
145                 }
146                 elsif ( my $regex_search =
147                     $input->param( $cgi_var_name . '_regex_search' ) )
148                 {
149                     $columns_with_regex->{$attr} = {
150                         search => $regex_search,
151                         replace =>
152                           $input->param( $cgi_var_name . '_regex_replace' ),
153                         modifiers =>
154                           $input->param( $cgi_var_name . '_regex_modifiers' )
155                     };
156                 }
157                 else {
158                     my @v =
159                       grep { $_ ne "" } uniq $input->multi_param($cgi_var_name);
160
161                     next unless @v;
162
163                     $new_item_data->{$attr} = join '|', @v;
164                 }
165             }
166         }
167
168         my $params = {
169             record_ids                        => \@itemnumbers,
170             regex_mod                         => $columns_with_regex,
171             new_values                        => $new_item_data,
172             exclude_from_local_holds_priority => (
173                 defined $exclude_from_local_holds_priority
174                   && $exclude_from_local_holds_priority ne ""
175               )
176             ? $exclude_from_local_holds_priority
177             : undef,
178
179         };
180         try {
181             my $job_id =
182               Koha::BackgroundJob::BatchUpdateItem->new->enqueue($params);
183             $nextop = 'enqueued';
184             $template->param( job_id => $job_id, );
185         }
186         catch {
187             push @messages,
188               {
189                 type  => 'error',
190                 code  => 'cannot_enqueue_job',
191                 error => $_,
192               };
193             $template->param( view => 'errors' );
194         };
195     }
196
197 }
198
199 $template->param(
200     messages => \@messages,
201 );
202 #
203 #-------------------------------------------------------------------------------
204 # build screen with existing items. and "new" one
205 #-------------------------------------------------------------------------------
206
207 if ($op eq "show"){
208     my $filefh = $input->upload('uploadfile');
209     my $filecontent = $input->param('filecontent');
210     my ( @notfoundbarcodes, @notfounditemnumbers);
211
212     my $split_chars = C4::Context->preference('BarcodeSeparators');
213     if ($filefh){
214         binmode $filefh, ':encoding(UTF-8)';
215         my @contentlist;
216         while (my $content=<$filefh>){
217             $content =~ s/[\r\n]*$//;
218             push @contentlist, $content if $content;
219         }
220
221         if ($filecontent eq 'barcode_file') {
222             @contentlist = grep /\S/, ( map { split /[$split_chars]/ } @contentlist );
223             @contentlist = uniq @contentlist;
224             # Note: adding lc for case insensitivity
225             my %itemdata = map { lc($_->{barcode}) => $_->{itemnumber} } @{ Koha::Items->search({ barcode => \@contentlist }, { columns => [ 'itemnumber', 'barcode' ] } )->unblessed };
226             @itemnumbers = map { exists $itemdata{lc $_} ? $itemdata{lc $_} : () } @contentlist;
227             @notfoundbarcodes = grep { !exists $itemdata{lc $_} } @contentlist;
228         }
229         elsif ( $filecontent eq 'itemid_file') {
230             @contentlist = uniq @contentlist;
231             my %itemdata = map { $_->{itemnumber} => 1 } @{ Koha::Items->search({ itemnumber => \@contentlist }, { columns => [ 'itemnumber' ] } )->unblessed };
232             @itemnumbers = grep { exists $itemdata{$_} } @contentlist;
233             @notfounditemnumbers = grep { !exists $itemdata{$_} } @contentlist;
234         }
235     } else {
236         if (defined $biblionumber && !@itemnumbers){
237             my $biblio = Koha::Biblios->find($biblionumber);
238             @itemnumbers = $biblio ? $biblio->items->get_column('itemnumber') : ();
239         }
240         if ( my $list = $input->param('barcodelist') ) {
241             my @barcodelist = grep /\S/, ( split /[$split_chars]/, $list );
242             @barcodelist = uniq @barcodelist;
243
244             @barcodelist = map { barcodedecode( $_ ) } @barcodelist;
245
246             # Note: adding lc for case insensitivity
247             my %itemdata = map { lc($_->{barcode}) => $_->{itemnumber} } @{ Koha::Items->search({ barcode => \@barcodelist }, { columns => [ 'itemnumber', 'barcode' ] } )->unblessed };
248             @itemnumbers = map { exists $itemdata{lc $_} ? $itemdata{lc $_} : () } @barcodelist;
249             @notfoundbarcodes = grep { !exists $itemdata{lc $_} } @barcodelist;
250         }
251     }
252
253     # Flag to tell the template there are valid results, hidden or not
254     if(scalar(@itemnumbers) > 0){ $template->param("itemresults" => 1); }
255     # Only display the items if there are no more than pref MaxItemsToProcessForBatchMod or MaxItemsToDisplayForBatchDel
256     my $max_display_items = $del
257         ? C4::Context->preference("MaxItemsToDisplayForBatchDel")
258         : C4::Context->preference("MaxItemsToDisplayForBatchMod");
259     $template->param("too_many_items_process" => scalar(@itemnumbers)) if !$del && scalar(@itemnumbers) > C4::Context->preference("MaxItemsToProcessForBatchMod");
260     if (scalar(@itemnumbers) <= ( $max_display_items // 1000 ) ) {
261         $display_items = 1;
262     } else {
263         $template->param("too_many_items_display" => scalar(@itemnumbers));
264         # Even if we do not display the items, we need the itemnumbers
265         $template->param(itemnumbers_array => \@itemnumbers);
266     }
267
268     # now, build the item form for entering a new item
269
270     # Getting list of subfields to keep when restricted batchmod edit is enabled
271     my @subfields_to_allow = $restrictededition ? split ' ', C4::Context->preference('SubfieldsToAllowForRestrictedBatchmod') : ();
272
273     my $subfields = Koha::UI::Form::Builder::Item->new->edit_form(
274         {
275             restricted_editition => $restrictededition,
276             (
277                 @subfields_to_allow
278                 ? ( subfields_to_allow => \@subfields_to_allow )
279                 : ()
280             ),
281             ignore_not_allowed_subfields => 1,
282             kohafields_to_ignore         => ['items.barcode'],
283             prefill_with_default_values => $use_default_values,
284             default_branches_empty      => 1,
285         }
286     );
287
288     # what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
289     $template->param(
290         subfields           => $subfields,
291         notfoundbarcodes    => \@notfoundbarcodes,
292         notfounditemnumbers => \@notfounditemnumbers
293     );
294     $nextop="action"
295 } # -- End action="show"
296
297 if ( $display_items ) {
298     my $items_table =
299       Koha::UI::Table::Builder::Items->new( { itemnumbers => \@itemnumbers } )
300       ->build_table;
301     $template->param(
302         items        => $items_table->{items},
303         item_header_loop => $items_table->{headers},
304     );
305 }
306
307 $template->param(
308     op  => $nextop,
309     del => $del,
310     ( $op ? ( $op => 1 ) : () ),
311     src          => $src,
312     biblionumber => $biblionumber,
313 );
314
315 output_html_with_http_headers $input, $cookie, $template->output;