From 86156da415141e32e16f2090477be32bda241afb Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Mon, 26 Jul 2021 18:30:33 +0200 Subject: [PATCH] Bug 28445: Adjust code to handle regexs Signed-off-by: Nick Clemens Signed-off-by: Tomas Cohen Arazi Signed-off-by: Jonathan Druart --- .../prog/en/includes/html_helpers.inc | 12 +- .../prog/en/modules/tools/batchMod-edit.tt | 20 +- tools/batchMod.pl | 277 ++++++++++-------- 3 files changed, 175 insertions(+), 134 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers.inc index 6aac6ae28e..0d90b49325 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers.inc @@ -116,7 +116,9 @@ [% ELSE %] ... [% END %] - [%- mv.javascript | $raw -%] + [% UNLESS no_plugin %][%# FIXME - from batchMod-edit, jQuery is included at the end of the template and cataloguing plugins are not working in this situation %] + [%- mv.javascript | $raw -%] + [% END %] [% END %] [% ELSIF ( mv.type == 'text' ) %] [% IF mv.readonly %] @@ -142,9 +144,9 @@ [% IF add_regex %] - s// - / - + s// + / + [% END %] @@ -162,7 +164,7 @@ [% IF add_regex %] [% IF (mv.type == 'text' || mv.type == 'text2' || mv.type == 'textarea' ) %] - RegEx + RegEx [% END %] [% 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 af9c38414d..05399cf7e0 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 @@ -248,7 +248,8 @@

Edit items

Checking the box right next to the subfield label will disable the entry and delete the subfield on all selected items. Leave fields blank to make no change.
- [% PROCESS subfields_for_item subfields => subfields, add_regex => 1, add_delete_checkbox => 1 %] + [%# no_plugin from batchMod-edit, jQuery is included at the end of the template and cataloguing plugins are not working in this situation %] + [% PROCESS subfields_for_item subfields => subfields, add_regex => 1, add_delete_checkbox => 1, no_plugin => 1 %]
@@ -310,8 +311,8 @@ $("input[name='disable_input']").click(function() { var row = $(this).attr("id"); row = row.replace("row","hint"); - var todisable = $(this).parent().find("[name='field_value'],[name='tag'],[name='subfield'],[name='mandatory']"); - var regex_link = $(this).parent().find("[name='field_regex']"); + var todisable = $(this).parent().find(".input_marceditor,.tag,.subfield,.mandatory"); + var regex_link = $(this).parent().find(".field_regex"); if ($(this).is(":checked")) { $(todisable).prop('disabled', true); $("#"+row).html(_("This subfield will be deleted")); @@ -322,12 +323,11 @@ $(regex_link).show(); } }); - $('a[name="field_regex"]').click(function() { - var id = $(this).attr('id'); - var editor = $(this).parent().find("[name='field_value']"); + $('a.field_regex').click(function() { + var editor = $(this).parent().find(".input_marceditor"); var tag_editor = $(this).parent().find(".buttonDot"); var regex = $(this).parent().find("[name='regex_fields']"); - var disable_input = $(this).parent().find("[name='disable_input']"); + var disable_input = $(this).parent().find(".disable_input"); if ($(this).html() == 'RegEx') { $(editor).hide(); $(regex).show(); @@ -348,10 +348,10 @@ [% IF ( show ) %] [%- # Loop over fields which may have plugin JavaScript -%] [%- UNLESS (too_many_items_process) -%] - [%- FOREACH ite IN item -%] - [% SET mv = ite.marc_value %] + [%- FOREACH subfield IN subfields -%] + [% SET mv = subfield.marc_value %] [%- IF ( mv.type == 'text2' ) -%] - + [% mv.javascript | $raw %] [%- END -%] [%- END -%] diff --git a/tools/batchMod.pl b/tools/batchMod.pl index 13ecde7347..acbbf295a6 100755 --- a/tools/batchMod.pl +++ b/tools/batchMod.pl @@ -117,48 +117,20 @@ if ($op eq "action") { 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'); - - # 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; - - #initializing values for updates - my ( $itemtagfield, $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" ); - if ($values_to_modify){ - my $xml = TransformHtmlToXml(\@tags,\@subfields,\@values,undef,undef, '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; my $del_biblionumbers; - try { - my $schema = Koha::Database->new->schema; - $schema->txn_do( - sub { - # For each item - my $i = 1; - foreach my $itemnumber (@itemnumbers) { - my $item = Koha::Items->find($itemnumber); - next - unless $item - ; # Should have been tested earlier, but just in case... - my $itemdata = $item->unblessed; - if ($del) { + if ( $del ) { + try { + my $schema = Koha::Database->new->schema; + $schema->txn_do( + sub { + foreach my $itemnumber (@itemnumbers) { + my $item = Koha::Items->find($itemnumber); + next + unless $item + ; # Should have been tested earlier, but just in case... + my $itemdata = $item->unblessed; my $return = $item->safe_delete; if ( ref( $return ) ) { $deleted_items++; @@ -192,100 +164,137 @@ 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); + if (@not_deleted) { + Koha::Exceptions::Exception->throw( + 'Some items have not been deleted, rolling back'); + } + } + ); + } + catch { + warn $_; + if ( $_->isa('Koha::Exceptions::Exception') ) { + $template->param( deletion_failed => 1 ); + } + die "Something terrible has happened!" + if ($_ =~ /Rollback failed/); # Rollback failed + }; + } - for ( my $i = 0 ; $i < @tags ; $i++ ) { - my $search = $searches[$i]; - next unless $search; + else { # modification - my $tag = $tags[$i]; - my $subfield = $subfields[$i]; - my $replace = $replaces[$i]; + my @columns = Koha::Items->columns; - my $value = $localmarcitem->field( $tag )->subfield( $subfield ); - my $old_value = $value; + my $new_item_data; + my @columns_with_regex; + for my $c ( @columns ) { + if ( $c eq 'more_subfields_xml' ) { + my @more_subfields_xml = $input->multi_param("items.more_subfields_xml"); + my @unlinked_item_subfields; + for my $subfield ( @more_subfields_xml ) { + my $v = $input->param('items.more_subfields_xml_' . $subfield); + push @unlinked_item_subfields, $subfield, $v; + } + if ( @unlinked_item_subfields ) { + my $marc = MARC::Record->new(); + # use of tag 999 is arbitrary, and doesn't need to match the item tag + # used in the framework + $marc->append_fields(MARC::Field->new('999', ' ', ' ', @unlinked_item_subfields)); + $marc->encoding("UTF-8"); + # FIXME This is WRONG! We need to use the values that haven't been modified by the batch tool! + $new_item_data->{more_subfields_xml} = $marc->as_xml("USMARC"); + next; + } + $new_item_data->{more_subfields_xml} = undef; + # FIXME deal with more_subfields_xml and @subfields_to_blank + } elsif ( grep { $c eq $_ } @subfields_to_blank ) { + # Empty this column + $new_item_data->{$c} = undef + } else { - 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 @v = grep { $_ ne "" } + uniq $input->multi_param( "items." . $c ); - 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 ); - } - } + next unless @v; + + $new_item_data->{$c} = join ' | ', @v; + } + + if ( my $regex_search = $input->param('items.'.$c.'_regex_search') ) { + push @columns_with_regex, $c; + } + } + + try { + my $schema = Koha::Database->new->schema; + $schema->txn_do( + sub { + + foreach my $itemnumber (@itemnumbers) { + my $item = Koha::Items->find($itemnumber); + next + unless $item + ; # Should have been tested earlier, but just in case... + my $itemdata = $item->unblessed; + + 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; } + } - $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'}; + my $modified = 0; + for my $c ( @columns_with_regex ) { + my $regex_search = $input->param('items.'.$c.'_regex_search'); + my $old_value = $item->$c; + + my $value = apply_regex( + { + search => $regex_search, + replace => $input->param( + 'items' . $c . '_regex_replace' + ), + modifiers => $input->param( + 'items' . $c . '_regex_modifiers' + ), + value => $old_value, + } + ); + unless ( $old_value eq $value ) { + $modified++; + $item->$c($value); } } + + $modified += scalar(keys %$new_item_data); # FIXME This is incorrect if old value == new value. Should we loop of the keys and compare the before/after values? + if ( $modified) { + my $itemlost_pre = $item->itemlost; + $item->set($new_item_data)->store({skip_record_index => 1}); + + push @$upd_biblionumbers, $itemdata->{'biblionumber'}; + + LostItem( + $item->itemnumber, 'batchmod', undef, + { skip_record_index => 1 } + ) if $item->itemlost + and not $itemlost_pre; + } + $modified_items++ if $modified || $modified_holds_priority; $modified_fields += $modified + $modified_holds_priority; } - $i++; - } - if (@not_deleted) { - Koha::Exceptions::Exception->throw( - 'Some items have not been deleted, rolling back'); } - } - ); - } - catch { - if ( $_->isa('Koha::Exceptions::Exception') ) { - $template->param( deletion_failed => 1 ); + ); } - die "Something terrible has happened!" - if ($_ =~ /Rollback failed/); # Rollback failed - }; + catch { + warn $_; + die "Something terrible has happened!" + if ($_ =~ /Rollback failed/); # Rollback failed + }; + } + $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once # Don't send specialUpdate for records we are going to delete @@ -320,6 +329,7 @@ if ($op eq "action") { $template->param( "job_completed" => 1 ); } + # Calling the template $template->param( modified_items => $modified_items, @@ -588,3 +598,32 @@ sub UpdateMarcWith { } return $modified; } + +sub apply_regex { + my ($params) = @_; + my $search = $params->{search}; + my $replace = $params->{replace}; + my $modifiers = $params->{modifiers} || []; + my $value = $params->{value}; + + my @available_modifiers = qw( i g ); + my $retained_modifiers = q||; + for my $modifier ( split //, @$modifiers ) { + $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/; + } + + return $value; +} -- 2.39.5