From 10f1366a806c5ea3c680d1913801b499d8950bfc Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Tue, 25 May 2021 10:31:36 +0200 Subject: [PATCH] Bug 28445: WIP - Use the task queue for the batch delete and update items tool --- Koha/BackgroundJob.pm | 8 +- Koha/BackgroundJob/BatchDeleteItem.pm | 140 ++++++++ Koha/BackgroundJob/BatchUpdateItem.pm | 313 ++++++++++++++++++ .../apache-shared-intranet-plack.conf | 1 - .../batch_item_record_modification.inc | 64 ++++ .../prog/en/modules/tools/batchMod-edit.tt | 40 ++- misc/background_jobs_worker.pl | 2 + tools/batchMod.pl | 229 ++----------- 8 files changed, 590 insertions(+), 207 deletions(-) create mode 100644 Koha/BackgroundJob/BatchDeleteItem.pm create mode 100644 Koha/BackgroundJob/BatchUpdateItem.pm create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc diff --git a/Koha/BackgroundJob.pm b/Koha/BackgroundJob.pm index 9b6de247387..e043d219734 100644 --- a/Koha/BackgroundJob.pm +++ b/Koha/BackgroundJob.pm @@ -26,8 +26,10 @@ use Koha::DateUtils qw( dt_from_string ); use Koha::Exceptions; use Koha::BackgroundJob::BatchUpdateBiblio; use Koha::BackgroundJob::BatchUpdateAuthority; +use Koha::BackgroundJob::BatchUpdateItem; use Koha::BackgroundJob::BatchDeleteBiblio; use Koha::BackgroundJob::BatchDeleteAuthority; +use Koha::BackgroundJob::BatchDeleteItem; use base qw( Koha::Object ); @@ -157,11 +159,15 @@ sub process { ? Koha::BackgroundJob::BatchUpdateBiblio->process($args) : $job_type eq 'batch_authority_record_modification' ? Koha::BackgroundJob::BatchUpdateAuthority->process($args) + : $job_type eq 'batch_item_record_modification' + ? Koha::BackgroundJob::BatchUpdateItem->process($args) : $job_type eq 'batch_biblio_record_deletion' ? Koha::BackgroundJob::BatchDeleteBiblio->process($args) : $job_type eq 'batch_authority_record_deletion' ? Koha::BackgroundJob::BatchDeleteAuthority->process($args) - : Koha::Exceptions::Exception->throw('->process called without valid job_type'); + : $job_type eq 'batch_item_record_deletion' + ? Koha::BackgroundJob::BatchDeleteItem->process($args) + : Koha::Exceptions::Exception->throw('->process called without valid job_type') } =head3 job_type diff --git a/Koha/BackgroundJob/BatchDeleteItem.pm b/Koha/BackgroundJob/BatchDeleteItem.pm new file mode 100644 index 00000000000..6ef0ee53669 --- /dev/null +++ b/Koha/BackgroundJob/BatchDeleteItem.pm @@ -0,0 +1,140 @@ +package Koha::BackgroundJob::BatchDeleteItem; + +use Modern::Perl; +use JSON qw( encode_json decode_json ); + +use Koha::BackgroundJobs; +use Koha::DateUtils qw( dt_from_string ); + +use base 'Koha::BackgroundJob'; + +sub job_type { + return 'batch_item_record_deletion'; +} + +sub process { + my ( $self, $args ) = @_; + + my $job_type = $args->{job_type}; + + my $job = Koha::BackgroundJobs->find( $args->{job_id} ); + + if ( !exists $args->{job_id} || !$job || $job->status eq 'cancelled' ) { + return; + } + + # FIXME If the job has already been started, but started again (worker has been restart for instance) + # Then we will start from scratch and so double delete the same records + + my $job_progress = 0; + $job->started_on(dt_from_string) + ->progress($job_progress) + ->status('started') + ->store; + + my @record_ids = @{ $args->{record_ids} }; + + my $report = { + total_records => scalar @record_ids, + total_success => 0, + }; + my @messages; + my $schema = Koha::Database->new->schema; + RECORD_IDS: for my $record_id ( sort { $a <=> $b } @record_ids ) { + + last if $job->get_from_storage->status eq 'cancelled'; + + try { + + my $schema = Koha::Database->new->schema; + $schema->txn_do( + sub { + my $item = Koha::Items->find($record_id); + next unless $item; + + my $biblionumber = $item->biblionumber; + my $return = $item->safe_delete; + if ( ref( $return ) ) { + $deleted_items++; + push @$upd_biblionumbers, $biblionumber; + } + else { + push @messages, { + type => 'error', + code => 'item_not_deleted', + itemnumber => $item->itemnumber + biblionumber => $biblionumber, + barcode => $item->barcode, + title => $item->biblio->title, + reason => $return, + } + } + + # If there are no items left, delete the biblio + if ($del_records) { + my $itemscount = Koha::Biblios->find( $biblionumber )->items->count; + if ( $itemscount == 0 ) { + my $error = C4::Biblio::DelBiblio( $biblionumber, { skip_record_index => 1 } ); + unless ($error) { + push @$del_biblionumbers, $biblionumber; + if ( $src eq 'CATALOGUING' ) { + # We are coming catalogue/detail.pl, there were items from a single bib record + $template->param( biblio_deleted => 1 ); + } + } + } + } + } + ); + } + catch { + + warn $_; + + if ( $_->isa('Koha::Exceptions::Exception') ) { + $template->param( deletion_failed => 1 ); + } + die "Something terrible has happened!" + if ($_ =~ /Rollback failed/); # Rollback failed + }; + + $report->{total_success}++; + $schema->storage->txn_commit; + $job->progress( ++$job_progress )->store; + } + + $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once + + # Don't send specialUpdate for records we are going to delete + my %del_bib_hash = map{ $_ => undef } @$del_biblionumbers; + @$upd_biblionumbers = grep( ! exists( $del_bib_hash{$_} ), @$upd_biblionumbers ); + + my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); + $indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers; + $indexer->index_records( $del_biblionumbers, 'recordDelete', "biblioserver", undef ) if @$del_biblionumbers; + + my $job_data = decode_json $job->data; + $job_data->{messages} = \@messages; + $job_data->{report} = $report; + + $job->ended_on(dt_from_string) + ->data(encode_json $job_data); + $job->status('finished') if $job->status ne 'cancelled'; + $job->store; +} + +sub enqueue { + my ( $self, $args) = @_; + + # TODO Raise exception instead + return unless exists $args->{record_ids}; + + my @record_ids = @{ $args->{record_ids} }; + + $self->SUPER::enqueue({ + job_size => scalar @record_ids, + job_args => {record_ids => \@record_ids,} + }); +} + +1; diff --git a/Koha/BackgroundJob/BatchUpdateItem.pm b/Koha/BackgroundJob/BatchUpdateItem.pm new file mode 100644 index 00000000000..8fe0a75764b --- /dev/null +++ b/Koha/BackgroundJob/BatchUpdateItem.pm @@ -0,0 +1,313 @@ +package Koha::BackgroundJob::BatchUpdateItem; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use JSON qw( encode_json decode_json ); +use List::MoreUtils qw( uniq ); +use Try::Tiny; + +use Koha::BackgroundJobs; +use Koha::DateUtils qw( dt_from_string ); +use Koha::SearchEngine::Indexer; + +use C4::Biblio; +use C4::Items; +use MARC::Record; +use MARC::Field; + +use base 'Koha::BackgroundJob'; + +=head1 NAME + +Koha::BackgroundJob::BatchUpdateItem - Batch update item records + +This is a subclass of Koha::BackgroundJob. + +=head1 API + +=head2 Class methods + +=head3 job_type + +Define the job type of this job: batch_item_record_modification + +=cut + +sub job_type { + return 'batch_item_record_modification'; +} + +=head3 process + +Process the modification. + +=cut + +sub process { + my ( $self, $args ) = @_; + + my $job = Koha::BackgroundJobs->find( $args->{job_id} ); + + if ( !exists $args->{job_id} || !$job || $job->status eq 'cancelled' ) { + return; + } + + # FIXME If the job has already been started, but started again (worker has been restart for instance) + # Then we will start from scratch and so double process the same records + + my $job_progress = 0; + $job->started_on(dt_from_string) + ->progress($job_progress) + ->status('started') + ->store; + + my @record_ids = @{ $args->{record_ids} }; + + my $report = { + total_records => scalar @record_ids, + total_success => 0, + }; + my @messages; + + my $tags = $args->{tags}; + my $subfields = $args->{subfield}; + my $values = $args->{values}; + my $indicator = $args->{indicator}; + my $ind_tag = $args->{ind_tag}; + my $searches = $args->{regex_search}; + my $replaces = $args->{regex_replace}; + my $modifiers = $args->{regex_modifiers}; + my $disabled = $args->{disabled}; + my $exclude_from_local_holds_priority = $args->{exclude_from_local_holds_priority}; + + # Is there something to modify ? + # TODO : We shall use this var to warn the user in case no modification was done to the items + my $values_to_modify = scalar(grep {!/^$/} @$values) || scalar(grep {!/^$/} @$searches); + my $values_to_blank = scalar(@$disabled); + + + #initializing values for updates + my ( $itemtagfield, $itemtagsubfield ) = C4::Biblio::GetMarcFromKohaField("items.itemnumber"); + my $marcitem; + if ($values_to_modify) { + my $xml = + C4::Biblio::TransformHtmlToXml( $tags, $subfields, $values, $indicator, + $ind_tag, 'ITEM' ); + $marcitem = MARC::Record::new_from_xml( $xml, 'UTF-8' ); + } + if ($values_to_blank) { + foreach my $disabledsubf (@$disabled) { + if ( $marcitem && $marcitem->field($itemtagfield) ) { + $marcitem->field($itemtagfield)->update( $disabledsubf => "" ); + } + else { + $marcitem = MARC::Record->new(); + $marcitem->append_fields( + MARC::Field->new( + $itemtagfield, '', '', $disabledsubf => "" + ) + ); + } + } + } + + my $upd_biblionumbers; + RECORD_IDS: for my $itemnumber ( sort { $a <=> $b } @record_ids ) { + + last if $job->get_from_storage->status eq 'cancelled'; + + try { + my $schema = Koha::Database->new->schema; + $schema->txn_do( + sub { + my $item = Koha::Items->find($itemnumber); + next unless $item; + + my $modified_holds_priority = 0; + if ( defined $exclude_from_local_holds_priority + && $exclude_from_local_holds_priority ne "" ) + { + if ( !defined $item->exclude_from_local_holds_priority + || $item->exclude_from_local_holds_priority != + $exclude_from_local_holds_priority ) + { + $item->exclude_from_local_holds_priority( + $exclude_from_local_holds_priority)->store; + $modified_holds_priority = 1; + } + } + my $modified = 0; + if ( $values_to_modify || $values_to_blank ) { + my $localmarcitem = C4::Items::Item2Marc($item->unblessed); + + for ( my $i = 0 ; $i < @$tags ; $i++ ) { + my $search = $searches->[$i]; + next unless $search; + + my $tag = $tags->[$i]; + my $subfield = $subfields->[$i]; + my $replace = $replaces->[$i]; + + my $value = $localmarcitem->field( $tag )->subfield( $subfield ); + my $old_value = $value; + + my @available_modifiers = qw( i g ); + my $retained_modifiers = q||; + for my $modifier ( split //, $modifiers->[$i] ) { + $retained_modifiers .= $modifier + if grep {/$modifier/} @available_modifiers; + } + if ( $retained_modifiers =~ m/^(ig|gi)$/ ) { + $value =~ s/$search/$replace/ig; + } + elsif ( $retained_modifiers eq 'i' ) { + $value =~ s/$search/$replace/i; + } + elsif ( $retained_modifiers eq 'g' ) { + $value =~ s/$search/$replace/g; + } + else { + $value =~ s/$search/$replace/; + } + + my @fields_to = $localmarcitem->field($tag); + foreach my $field_to_update ( @fields_to ) { + unless ( $old_value eq $value ) { + $modified++; + $field_to_update->update( $subfield => $value ); + } + } + } + + $modified += UpdateMarcWith( $marcitem, $localmarcitem ); + if ($modified) { + eval { + if ( + my $item = ModItemFromMarc( + $localmarcitem, + $item->biblionumber, + $itemnumber, + { skip_record_index => 1 }, + ) + ) + { + LostItem( + $itemnumber, + 'batchmod', + undef, + { skip_record_index => 1 } + ) if $item->{itemlost} + and not $item->itemlost; + } + }; + push @$upd_biblionumbers, $item->biblionumber; + } + } + } + ); + + $report->{modified_items}++ if $modified || $modified_holds_priority; + $report->{modified_fields} += $modified + $modified_holds_priority; + + push @messages, { + type => 'success', + code => 'item_modified', + itemnumber => $item->itemnumber, + biblionumber => $item->biblionumber, + }; + $report->{total_success}++; + + } catch { + warn $_; + push @messages, { + type => 'error', + code => 'item_not_modified', + itemnumber => $item->itemnumber, + biblionumber => $item->biblionumber, + }; + + }; + + $job->progress( ++$job_progress )->store; + } + + $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once + my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); + $indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers; + + my $job_data = decode_json $job->data; + $job_data->{messages} = \@messages; + $job_data->{report} = $report; + + $job->ended_on(dt_from_string) + ->data(encode_json $job_data); + $job->status('finished') if $job->status ne 'cancelled'; + $job->store; +} + +=head3 enqueue + +Enqueue the new job + +=cut + +sub enqueue { + my ( $self, $args) = @_; + + # TODO Raise exception instead + return unless exists $args->{record_ids}; + + my @record_ids = @{ $args->{record_ids} }; + + $self->SUPER::enqueue({ + job_size => scalar @record_ids, + job_args => {%$args}, + }); +} + +#BE WARN : it is not the general case +# This function can be OK in the item marc record special case +# Where subfield is not repeated +# And where we are sure that field should correspond +# And $tag>10 +sub UpdateMarcWith { + my ($marcfrom,$marcto)=@_; + my ( $itemtag, $itemtagsubfield) = C4::Biblio::GetMarcFromKohaField( "items.itemnumber" ); + my $fieldfrom=$marcfrom->field($itemtag); + my @fields_to=$marcto->field($itemtag); + my $modified = 0; + + return $modified unless $fieldfrom; + + foreach my $subfield ( $fieldfrom->subfields() ) { + foreach my $field_to_update ( @fields_to ) { + if ( $subfield->[1] ) { + unless ( $field_to_update->subfield($subfield->[0]) eq $subfield->[1] ) { + $modified++; + $field_to_update->update( $subfield->[0] => $subfield->[1] ); + } + } + else { + $modified++; + $field_to_update->delete_subfield( code => $subfield->[0] ); + } + } + } + return $modified; +} + +1; diff --git a/debian/templates/apache-shared-intranet-plack.conf b/debian/templates/apache-shared-intranet-plack.conf index 029eca8d537..311b5d7f37d 100644 --- a/debian/templates/apache-shared-intranet-plack.conf +++ b/debian/templates/apache-shared-intranet-plack.conf @@ -13,7 +13,6 @@ # don't break under plack/starman ProxyPass "/cgi-bin/koha/offline_circ/process_koc.pl" "!" ProxyPass "/cgi-bin/koha/tools/background-job-progress.pl" "!" - ProxyPass "/cgi-bin/koha/tools/batchMod.pl" "!" ProxyPass "/cgi-bin/koha/tools/export.pl" "!" ProxyPass "/cgi-bin/koha/tools/manage-marc-import.pl" "!" ProxyPass "/cgi-bin/koha/tools/stage-marc-import.pl" "!" diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc new file mode 100644 index 00000000000..51a447e3b2c --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc @@ -0,0 +1,64 @@ +[% BLOCK report %] + [% SET report = job.report %] + [% IF report %] + [% IF report.total_records == report.total_success %] +
+ All records have successfully been modified! New batch record modification + [% IF lists.count %] +
+ Add modified records to the following list: + + [% END %] +
+ [% ELSE %] +
+ [% report.total_success | html %] / [% report.total_records | html %] records have successfully been modified. Some errors occurred. + [% IF job.status == 'cancelled' %]The job has been cancelled before it finished.[% END %] + New batch record modification +
+ [% END %] + [% END %] +[% END %] + +[% BLOCK detail %] + [% FOR m IN job.messages %] +
+ [% IF m.type == 'success' %] + + [% ELSIF m.type == 'warning' %] + + [% ELSIF m.type == 'error' %] + + [% END %] + [% SWITCH m.code %] + [% CASE 'biblio_not_modified' %] + Bibliographic record [% m.biblionumber | html %] has not been modified. An error occurred on modifying it.[% IF m.error %] ([% m.error | html %])[% END %]. + [% CASE 'biblio_modified' %] + Bibliographic record [% m.biblionumber | html %] has successfully been modified. + [% END %] +
+ [% END %] +[% END %] + +[% BLOCK js %] + $("#add_bibs_to_list").change(function(){ + var selected = $("#add_bibs_to_list").find("option:selected"); + if ( selected.attr("class") == "shelf" ){ + var shelfnumber = selected.attr("value"); + var bibs = new Array(); + [% FOREACH message IN job.messages %] + [% IF message.code == 'biblio_modified' %] + bibs.push("biblionumber="+[% message.biblionumber | html %]); + [% END %] + [% END %] + var bibstring = bibs.join("&"); + window.open('/cgi-bin/koha/virtualshelves/addbybiblionumber.pl?shelfnumber='+shelfnumber+'&confirm=1&'+bibstring, 'popup', 'width=500,height=500,toolbar=false,scrollbars=yes,resizeable=yes'); + return false; + } + }); +[% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt index e07e44b2280..5ac6499632c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt @@ -31,16 +31,15 @@
- [% IF ( show ) %] -

Batch item modification

- [% ELSE %] -

Batch item modification results

+

Batch item modification

+ [% IF op == 'enqueued' %]
- [% IF (modified_items) %] - [% modified_items | html %] item(s) modified (with [% modified_fields | html %] field(s) modified). - [% ELSE %] - No items modified. - [% END %] +

The job has been enqueued! It will be processed as soon as possible.

+

View detail of the enqueued job + | New batch item modification

+
+ [% END %] +
[% IF src == 'CATALOGUING' # from catalogue/detail.pl > Edit items in a batch%] [% IF searchid %] @@ -55,7 +54,24 @@ [% END %]
- [% END # /IF show %] + + [% FOREACH message IN messages %] + [% IF message.type == 'success' %] +
+ [% ELSIF message.type == 'warning' %] +
+ [% ELSIF message.type == 'error' %] +
+ [% END %] + [% IF message.code == 'cannot_enqueue_job' %] + Cannot enqueue this job. + [% END %] + [% IF message.error %] + (The error was: [% message.error | html %], see the Koha log file for more information). + [% END %] +
+ [% END %] + [% IF ( barcode_not_unique ) %]
@@ -151,6 +167,8 @@ [% END %]

+[#% TODO We need to refactor the pl to make this reusable from batchMod and BatchUpdateItem %] + [% PROCESS item_table headers => item_header_loop, items => item_loop %] @@ -359,7 +377,7 @@ Return to batch item modification [% END %] - [% END #/IF show %] + [% END %] [% MACRO jsinclude BLOCK %] diff --git a/misc/background_jobs_worker.pl b/misc/background_jobs_worker.pl index 0642faf71bb..bde1e8e473f 100755 --- a/misc/background_jobs_worker.pl +++ b/misc/background_jobs_worker.pl @@ -31,8 +31,10 @@ try { my @job_types = qw( batch_biblio_record_modification batch_authority_record_modification + batch_item_record_modification batch_biblio_record_deletion batch_authority_record_deletion + batch_item_record_deletion ); if ( $conn ) { diff --git a/tools/batchMod.pl b/tools/batchMod.pl index fa4a86ec967..8f5c27fcb4e 100755 --- a/tools/batchMod.pl +++ b/tools/batchMod.pl @@ -45,6 +45,8 @@ use Koha::Items; use Koha::ItemTypes; use Koha::Patrons; use Koha::SearchEngine::Indexer; +use Koha::BackgroundJob::BatchDeleteItem; +use Koha::BackgroundJob::BatchUpdateItem; my $input = CGI->new; my $dbh = C4::Context->dbh; @@ -102,74 +104,13 @@ my $modified_fields = 0; # Numbers of modified fields my %cookies = parse CGI::Cookie($cookie); my $sessionID = $cookies{'CGISESSID'}->value; +my @messages; #--- ---------------------------------------------------------------------------- if ($op eq "action") { #------------------------------------------------------------------------------- - my @tags = $input->multi_param('tag'); - my @subfields = $input->multi_param('subfield'); - my @values = $input->multi_param('field_value'); - my @searches = $input->multi_param('regex_search'); - my @replaces = $input->multi_param('regex_replace'); - my @modifiers = $input->multi_param('regex_modifiers'); - my @disabled = $input->multi_param('disable_input'); - # build indicator hash. - my @ind_tag = $input->multi_param('ind_tag'); - my @indicator = $input->multi_param('indicator'); - - # Is there something to modify ? - # TODO : We shall use this var to warn the user in case no modification was done to the items - my $values_to_modify = scalar(grep {!/^$/} @values) || scalar(grep {!/^$/} @searches); - my $values_to_blank = scalar(@disabled); - - my $marcitem; - - # Once the job is done - if ($completedJobID) { - # If we have a reasonable amount of items, we display them - my $max_items = $del ? C4::Context->preference("MaxItemsToDisplayForBatchDel") : C4::Context->preference("MaxItemsToDisplayForBatchMod"); - if (scalar(@itemnumbers) <= $max_items ){ - if (scalar(@itemnumbers) <= 1000 ) { - $items_display_hashref=BuildItemsData(@itemnumbers); - } else { - # Else, we only display the barcode - my @simple_items_display = map { - my $itemnumber = $_; - my $item = Koha::Items->find($itemnumber); - { - itemnumber => $itemnumber, - barcode => $item ? ( $item->barcode // q{} ) : q{}, - biblionumber => $item ? $item->biblio->biblionumber : q{}, - }; - } @itemnumbers; - $template->param("simple_items_display" => \@simple_items_display); - } - } else { - $template->param( "too_many_items_display" => scalar(@itemnumbers) ); - $template->param( "job_completed" => 1 ); - } - - } else { - # While the job is getting done - - #initializing values for updates - my ( $itemtagfield, $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" ); - if ($values_to_modify){ - my $xml = TransformHtmlToXml(\@tags,\@subfields,\@values,\@indicator,\@ind_tag, 'ITEM'); - $marcitem = MARC::Record::new_from_xml($xml, 'UTF-8'); - } - if ($values_to_blank){ - foreach my $disabledsubf (@disabled){ - if ($marcitem && $marcitem->field($itemtagfield)){ - $marcitem->field($itemtagfield)->update( $disabledsubf => "" ); - } - else { - $marcitem = MARC::Record->new(); - $marcitem->append_fields( MARC::Field->new( $itemtagfield, '', '', $disabledsubf => "" ) ); - } - } - } + if ( $del ) { my $upd_biblionumbers; my $del_biblionumbers; try { @@ -218,84 +159,6 @@ if ($op eq "action") { } } } - else { - my $modified_holds_priority = 0; - if ( defined $exclude_from_local_holds_priority && $exclude_from_local_holds_priority ne "" ) { - if(!defined $item->exclude_from_local_holds_priority || $item->exclude_from_local_holds_priority != $exclude_from_local_holds_priority) { - $item->exclude_from_local_holds_priority($exclude_from_local_holds_priority)->store; - $modified_holds_priority = 1; - } - } - my $modified = 0; - if ( $values_to_modify || $values_to_blank ) { - my $localmarcitem = Item2Marc($itemdata); - - for ( my $i = 0 ; $i < @tags ; $i++ ) { - my $search = $searches[$i]; - next unless $search; - - my $tag = $tags[$i]; - my $subfield = $subfields[$i]; - my $replace = $replaces[$i]; - - my $value = $localmarcitem->field( $tag )->subfield( $subfield ); - my $old_value = $value; - - my @available_modifiers = qw( i g ); - my $retained_modifiers = q||; - for my $modifier ( split //, $modifiers[$i] ) { - $retained_modifiers .= $modifier - if grep {/$modifier/} @available_modifiers; - } - if ( $retained_modifiers =~ m/^(ig|gi)$/ ) { - $value =~ s/$search/$replace/ig; - } - elsif ( $retained_modifiers eq 'i' ) { - $value =~ s/$search/$replace/i; - } - elsif ( $retained_modifiers eq 'g' ) { - $value =~ s/$search/$replace/g; - } - else { - $value =~ s/$search/$replace/; - } - - my @fields_to = $localmarcitem->field($tag); - foreach my $field_to_update ( @fields_to ) { - unless ( $old_value eq $value ) { - $modified++; - $field_to_update->update( $subfield => $value ); - } - } - } - - $modified += UpdateMarcWith( $marcitem, $localmarcitem ); - if ($modified) { - eval { - if ( - my $item = ModItemFromMarc( - $localmarcitem, - $itemdata->{biblionumber}, - $itemnumber, - { skip_record_index => 1 }, - ) - ) - { - LostItem( - $itemnumber, - 'batchmod', - undef, - { skip_record_index => 1 } - ) if $item->{itemlost} - and not $itemdata->{itemlost}; - } - }; - push @$upd_biblionumbers, $itemdata->{'biblionumber'}; - } - } - $modified_items++ if $modified || $modified_holds_priority; - $modified_fields += $modified + $modified_holds_priority; - } $i++; } if (@not_deleted) { @@ -321,15 +184,40 @@ if ($op eq "action") { my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); $indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers; $indexer->index_records( $del_biblionumbers, 'recordDelete', "biblioserver", undef ) if @$del_biblionumbers; + } else { + my $params = { + tags => [ $input->multi_param('tag') ], + subfields => [ $input->multi_param('subfield') ], + values => [ $input->multi_param('field_value') ], + searches => [ $input->multi_param('regex_search') ], + replaces => [ $input->multi_param('regex_replace') ], + modifiers => [ $input->multi_param('regex_modifiers') ], + disabled => [ $input->multi_param('disable_input') ], + ind_tag => [ $input->multi_param('ind_tag') ], + indicator => [ $input->multi_param('indicator') ], + record_ids => \@itemnumbers, + }; + try { + my $job_id = Koha::BackgroundJob::BatchUpdateItem->new->enqueue($params); + $nextop = 'enqueued'; + $template->param( + job_id => $job_id, + ); + } catch { + push @messages, { + type => 'error', + code => 'cannot_enqueue_job', + error => $_, + }; + $template->param( view => 'errors' ); + }; } - # Calling the template - $template->param( - modified_items => $modified_items, - modified_fields => $modified_fields, - ); - } + +$template->param( + messages => \@messages, +); # #------------------------------------------------------------------------------- # build screen with existing items. and "new" one @@ -719,50 +607,3 @@ sub BuildItemsData{ return { item_loop => \@item_value_loop, item_header_loop => \@header_loop }; } - -#BE WARN : it is not the general case -# This function can be OK in the item marc record special case -# Where subfield is not repeated -# And where we are sure that field should correspond -# And $tag>10 -sub UpdateMarcWith { - my ($marcfrom,$marcto)=@_; - my ( $itemtag, $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" ); - my $fieldfrom=$marcfrom->field($itemtag); - my @fields_to=$marcto->field($itemtag); - my $modified = 0; - - return $modified unless $fieldfrom; - - foreach my $subfield ( $fieldfrom->subfields() ) { - foreach my $field_to_update ( @fields_to ) { - if ( $subfield->[1] ) { - unless ( $field_to_update->subfield($subfield->[0]) eq $subfield->[1] ) { - $modified++; - $field_to_update->update( $subfield->[0] => $subfield->[1] ); - } - } - else { - $modified++; - $field_to_update->delete_subfield( code => $subfield->[0] ); - } - } - } - return $modified; -} - -sub find_value { - my ($tagfield,$insubfield,$record) = @_; - my $result; - my $indicator; - foreach my $field ($record->field($tagfield)) { - my @subfields = $field->subfields(); - foreach my $subfield (@subfields) { - if (@$subfield[0] eq $insubfield) { - $result .= @$subfield[1]; - $indicator = $field->indicator(1).$field->indicator(2); - } - } - } - return($indicator,$result); -} -- 2.20.1