View | Details | Raw Unified | Return to bug 28445
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers.inc (-5 / +7 lines)
Lines 116-122 Link Here
116
                            [% ELSE  %]
116
                            [% ELSE  %]
117
                                <a href="#" id="buttonDot_[%- mv.id | html -%]" class="[%- mv.class | html -%]" title="Tag editor">...</a>
117
                                <a href="#" id="buttonDot_[%- mv.id | html -%]" class="[%- mv.class | html -%]" title="Tag editor">...</a>
118
                            [% END %]
118
                            [% END %]
119
                            [%- mv.javascript | $raw -%]
119
                            [% 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 %]
120
                                [%- mv.javascript | $raw -%]
121
                            [% END %]
120
                        [% END %]
122
                        [% END %]
121
                    [% ELSIF ( mv.type == 'text' ) %]
123
                    [% ELSIF ( mv.type == 'text' ) %]
122
                        [% IF mv.readonly %]
124
                        [% IF mv.readonly %]
Lines 142-150 Link Here
142
144
143
                    [% IF add_regex %]
145
                    [% IF add_regex %]
144
                        <span name="regex_fields" style="display: none;">
146
                        <span name="regex_fields" style="display: none;">
145
                            s/<input type="text" id="[% mv.id | html %]" name="regex_search" placeholder="regex pattern" />/
147
                            s/<input type="text" name="[% kohafield | html %]_regex_search" class="regex_search" placeholder="regex pattern" />/
146
                            <input type="text" id="[% mv.id | html %]" name="regex_replace" placeholder="regex replacement" />/
148
                            <input type="text" name="[% kohafield | html %]_regex_replace" class="regex_replace" placeholder="regex replacement" />/
147
                            <input type="text" id="[% mv.id | html %]" name="regex_modifiers" placeholder="ig" size="3" />
149
                            <input type="text" name="[% kohafield | html %]_regex_modifiers" class="regex_modifiers" placeholder="ig" size="3" />
148
                        </span>
150
                        </span>
149
                    [% END %]
151
                    [% END %]
150
152
Lines 162-168 Link Here
162
164
163
                    [% IF add_regex %]
165
                    [% IF add_regex %]
164
                        [% IF (mv.type == 'text' || mv.type == 'text2' || mv.type == 'textarea' ) %]
166
                        [% IF (mv.type == 'text' || mv.type == 'text2' || mv.type == 'textarea' ) %]
165
                            <a href="#" name="field_regex" id="[% subfield.id | html %]" >RegEx</a>
167
                            <a href="#" name="[% kohafield | html %]_field_regex" class="field_regex" id="[% subfield.id | html %]" >RegEx</a>
166
                        [% END %]
168
                        [% END %]
167
                    [% END %]
169
                    [% END %]
168
170
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt (-10 / +10 lines)
Lines 248-254 Link Here
248
                        <h2>Edit items</h2>
248
                        <h2>Edit items</h2>
249
                        <div class="hint">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.</div>
249
                        <div class="hint">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.</div>
250
                        <fieldset class="rows">
250
                        <fieldset class="rows">
251
                            [% PROCESS subfields_for_item subfields => subfields, add_regex => 1, add_delete_checkbox => 1 %]
251
                            [%# no_plugin from batchMod-edit, jQuery is included at the end of the template and cataloguing plugins are not working in this situation %]
252
                            [% PROCESS subfields_for_item subfields => subfields, add_regex => 1, add_delete_checkbox => 1, no_plugin => 1 %]
252
                        </fieldset>
253
                        </fieldset>
253
254
254
                        <fieldset class="rows">
255
                        <fieldset class="rows">
Lines 310-317 Link Here
310
                $("input[name='disable_input']").click(function() {
311
                $("input[name='disable_input']").click(function() {
311
                    var row = $(this).attr("id");
312
                    var row = $(this).attr("id");
312
                    row = row.replace("row","hint");
313
                    row = row.replace("row","hint");
313
                    var todisable = $(this).parent().find("[name='field_value'],[name='tag'],[name='subfield'],[name='mandatory']");
314
                    var todisable = $(this).parent().find(".input_marceditor,.tag,.subfield,.mandatory");
314
                    var regex_link = $(this).parent().find("[name='field_regex']");
315
                    var regex_link = $(this).parent().find(".field_regex");
315
                    if ($(this).is(":checked")) {
316
                    if ($(this).is(":checked")) {
316
                        $(todisable).prop('disabled', true);
317
                        $(todisable).prop('disabled', true);
317
                        $("#"+row).html(_("This subfield will be deleted"));
318
                        $("#"+row).html(_("This subfield will be deleted"));
Lines 322-333 Link Here
322
                        $(regex_link).show();
323
                        $(regex_link).show();
323
                    }
324
                    }
324
                });
325
                });
325
                $('a[name="field_regex"]').click(function() {
326
                $('a.field_regex').click(function() {
326
                    var id = $(this).attr('id');
327
                    var editor = $(this).parent().find(".input_marceditor");
327
                    var editor = $(this).parent().find("[name='field_value']");
328
                    var tag_editor = $(this).parent().find(".buttonDot");
328
                    var tag_editor = $(this).parent().find(".buttonDot");
329
                    var regex = $(this).parent().find("[name='regex_fields']");
329
                    var regex = $(this).parent().find("[name='regex_fields']");
330
                    var disable_input = $(this).parent().find("[name='disable_input']");
330
                    var disable_input = $(this).parent().find(".disable_input");
331
                    if ($(this).html() == 'RegEx') {
331
                    if ($(this).html() == 'RegEx') {
332
                        $(editor).hide();
332
                        $(editor).hide();
333
                        $(regex).show();
333
                        $(regex).show();
Lines 348-357 Link Here
348
        [% IF ( show ) %]
348
        [% IF ( show ) %]
349
            [%- # Loop over fields which may have plugin JavaScript -%]
349
            [%- # Loop over fields which may have plugin JavaScript -%]
350
            [%- UNLESS (too_many_items_process) -%]
350
            [%- UNLESS (too_many_items_process) -%]
351
                [%- FOREACH ite IN item -%]
351
                [%- FOREACH subfield IN subfields -%]
352
                    [% SET mv = ite.marc_value %]
352
                    [% SET mv = subfield.marc_value %]
353
                    [%- IF ( mv.type == 'text2' ) -%]
353
                    [%- IF ( mv.type == 'text2' ) -%]
354
                        <!-- subfield[% ite.tag | html %][% ite.subfield | html %][% ite.random | html %] -->
354
                        <!-- subfield[% subfield.tag | html %][% subfield.subfield | html %][% subfield.random | html %] -->
355
                        [% mv.javascript | $raw %]
355
                        [% mv.javascript | $raw %]
356
                    [%- END -%]
356
                    [%- END -%]
357
                [%- END -%]
357
                [%- END -%]
(-)a/tools/batchMod.pl (-120 / +158 lines)
Lines 117-164 if ($op eq "action") { Link Here
117
    my @searches  = $input->multi_param('regex_search');
117
    my @searches  = $input->multi_param('regex_search');
118
    my @replaces  = $input->multi_param('regex_replace');
118
    my @replaces  = $input->multi_param('regex_replace');
119
    my @modifiers = $input->multi_param('regex_modifiers');
119
    my @modifiers = $input->multi_param('regex_modifiers');
120
    my @disabled  = $input->multi_param('disable_input');
121
122
    # Is there something to modify ?
123
    # TODO : We shall use this var to warn the user in case no modification was done to the items
124
    my $values_to_modify = scalar(grep {!/^$/} @values) || scalar(grep {!/^$/} @searches);
125
    my $values_to_blank  = scalar(@disabled);
126
127
    my $marcitem;
128
129
    #initializing values for updates
130
    my (  $itemtagfield,   $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" );
131
    if ($values_to_modify){
132
        my $xml = TransformHtmlToXml(\@tags,\@subfields,\@values,undef,undef, 'ITEM');
133
        $marcitem = MARC::Record::new_from_xml($xml, 'UTF-8');
134
    }
135
    if ($values_to_blank){
136
        foreach my $disabledsubf (@disabled){
137
            if ($marcitem && $marcitem->field($itemtagfield)){
138
                $marcitem->field($itemtagfield)->update( $disabledsubf => "" );
139
            }
140
            else {
141
                $marcitem = MARC::Record->new();
142
                $marcitem->append_fields( MARC::Field->new( $itemtagfield, '', '', $disabledsubf => "" ) );
143
            }
144
        }
145
    }
146
120
147
    my $upd_biblionumbers;
121
    my $upd_biblionumbers;
148
    my $del_biblionumbers;
122
    my $del_biblionumbers;
149
    try {
123
    if ( $del ) {
150
        my $schema = Koha::Database->new->schema;
124
        try {
151
        $schema->txn_do(
125
            my $schema = Koha::Database->new->schema;
152
            sub {
126
            $schema->txn_do(
153
                # For each item
127
                sub {
154
                my $i = 1;
128
                    foreach my $itemnumber (@itemnumbers) {
155
                foreach my $itemnumber (@itemnumbers) {
129
                        my $item = Koha::Items->find($itemnumber);
156
                    my $item = Koha::Items->find($itemnumber);
130
                        next
157
                    next
131
                          unless $item
158
                      unless $item
132
                          ; # Should have been tested earlier, but just in case...
159
                      ; # Should have been tested earlier, but just in case...
133
                        my $itemdata = $item->unblessed;
160
                    my $itemdata = $item->unblessed;
161
                    if ($del) {
162
                        my $return = $item->safe_delete;
134
                        my $return = $item->safe_delete;
163
                        if ( ref( $return ) ) {
135
                        if ( ref( $return ) ) {
164
                            $deleted_items++;
136
                            $deleted_items++;
Lines 192-291 if ($op eq "action") { Link Here
192
                            }
164
                            }
193
                        }
165
                        }
194
                    }
166
                    }
195
                    else {
167
                    if (@not_deleted) {
196
                        my $modified_holds_priority = 0;
168
                        Koha::Exceptions::Exception->throw(
197
                        if ( defined $exclude_from_local_holds_priority && $exclude_from_local_holds_priority ne "" ) {
169
                            'Some items have not been deleted, rolling back');
198
                            if(!defined $item->exclude_from_local_holds_priority || $item->exclude_from_local_holds_priority != $exclude_from_local_holds_priority) {
170
                    }
199
                            $item->exclude_from_local_holds_priority($exclude_from_local_holds_priority)->store;
171
                }
200
                            $modified_holds_priority = 1;
172
            );
201
                        }
173
        }
202
                        }
174
        catch {
203
                        my $modified = 0;
175
            warn $_;
204
                        if ( $values_to_modify || $values_to_blank ) {
176
            if ( $_->isa('Koha::Exceptions::Exception') ) {
205
                            my $localmarcitem = Item2Marc($itemdata);
177
                $template->param( deletion_failed => 1 );
178
            }
179
            die "Something terrible has happened!"
180
                if ($_ =~ /Rollback failed/); # Rollback failed
181
        };
182
    }
206
183
207
                            for ( my $i = 0 ; $i < @tags ; $i++ ) {
184
    else { # modification
208
                                my $search = $searches[$i];
209
                                next unless $search;
210
185
211
                                my $tag = $tags[$i];
186
        my @columns = Koha::Items->columns;
212
                                my $subfield = $subfields[$i];
213
                                my $replace = $replaces[$i];
214
187
215
                                my $value = $localmarcitem->field( $tag )->subfield( $subfield );
188
        my $new_item_data;
216
                                my $old_value = $value;
189
        my @columns_with_regex;
190
        for my $c ( @columns ) {
191
            if ( $c eq 'more_subfields_xml' ) {
192
                my @more_subfields_xml = $input->multi_param("items.more_subfields_xml");
193
                my @unlinked_item_subfields;
194
                for my $subfield ( @more_subfields_xml ) {
195
                    my $v = $input->param('items.more_subfields_xml_' . $subfield);
196
                    push @unlinked_item_subfields, $subfield, $v;
197
                }
198
                if ( @unlinked_item_subfields ) {
199
                    my $marc = MARC::Record->new();
200
                    # use of tag 999 is arbitrary, and doesn't need to match the item tag
201
                    # used in the framework
202
                    $marc->append_fields(MARC::Field->new('999', ' ', ' ', @unlinked_item_subfields));
203
                    $marc->encoding("UTF-8");
204
                    # FIXME This is WRONG! We need to use the values that haven't been modified by the batch tool!
205
                    $new_item_data->{more_subfields_xml} = $marc->as_xml("USMARC");
206
                    next;
207
                }
208
                $new_item_data->{more_subfields_xml} = undef;
209
                # FIXME deal with more_subfields_xml and @subfields_to_blank
210
            } elsif ( grep { $c eq $_ } @subfields_to_blank ) {
211
                # Empty this column
212
                $new_item_data->{$c} = undef
213
            } else {
217
214
218
                                my @available_modifiers = qw( i g );
215
                my @v = grep { $_ ne "" }
219
                                my $retained_modifiers = q||;
216
                    uniq $input->multi_param( "items." . $c );
220
                                for my $modifier ( split //, $modifiers[$i] ) {
221
                                    $retained_modifiers .= $modifier
222
                                        if grep {/$modifier/} @available_modifiers;
223
                                }
224
                                if ( $retained_modifiers =~ m/^(ig|gi)$/ ) {
225
                                    $value =~ s/$search/$replace/ig;
226
                                }
227
                                elsif ( $retained_modifiers eq 'i' ) {
228
                                    $value =~ s/$search/$replace/i;
229
                                }
230
                                elsif ( $retained_modifiers eq 'g' ) {
231
                                    $value =~ s/$search/$replace/g;
232
                                }
233
                                else {
234
                                    $value =~ s/$search/$replace/;
235
                                }
236
217
237
                                my @fields_to = $localmarcitem->field($tag);
218
                next unless @v;
238
                                foreach my $field_to_update ( @fields_to ) {
219
239
                                    unless ( $old_value eq $value ) {
220
                $new_item_data->{$c} = join ' | ', @v;
240
                                        $modified++;
221
            }
241
                                        $field_to_update->update( $subfield => $value );
222
242
                                    }
223
            if ( my $regex_search = $input->param('items.'.$c.'_regex_search') ) {
243
                                }
224
                push @columns_with_regex, $c;
225
            }
226
        }
227
228
        try {
229
            my $schema = Koha::Database->new->schema;
230
            $schema->txn_do(
231
                sub {
232
233
                    foreach my $itemnumber (@itemnumbers) {
234
                        my $item = Koha::Items->find($itemnumber);
235
                        next
236
                          unless $item
237
                          ; # Should have been tested earlier, but just in case...
238
                        my $itemdata = $item->unblessed;
239
240
                        my $modified_holds_priority = 0;
241
                        if ( defined $exclude_from_local_holds_priority && $exclude_from_local_holds_priority ne "" ) {
242
                            if(!defined $item->exclude_from_local_holds_priority || $item->exclude_from_local_holds_priority != $exclude_from_local_holds_priority) {
243
                                $item->exclude_from_local_holds_priority($exclude_from_local_holds_priority)->store;
244
                                $modified_holds_priority = 1;
244
                            }
245
                            }
246
                        }
245
247
246
                            $modified += UpdateMarcWith( $marcitem, $localmarcitem );
248
                        my $modified = 0;
247
                            if ($modified) {
249
                        for my $c ( @columns_with_regex ) {
248
                                eval {
250
                            my $regex_search = $input->param('items.'.$c.'_regex_search');
249
                                    if (
251
                            my $old_value = $item->$c;
250
                                        my $item = ModItemFromMarc(
252
251
                                            $localmarcitem,
253
                            my $value = apply_regex(
252
                                            $itemdata->{biblionumber},
254
                                {
253
                                            $itemnumber,
255
                                    search  => $regex_search,
254
                                            { skip_record_index => 1 },
256
                                    replace => $input->param(
255
                                        )
257
                                        'items' . $c . '_regex_replace'
256
                                      )
258
                                    ),
257
                                    {
259
                                    modifiers => $input->param(
258
                                        LostItem(
260
                                        'items' . $c . '_regex_modifiers'
259
                                            $itemnumber,
261
                                    ),
260
                                            'batchmod',
262
                                    value => $old_value,
261
                                            undef,
263
                                }
262
                                            { skip_record_index => 1 }
264
                            );
263
                                        ) if $item->{itemlost}
265
                            unless ( $old_value eq $value ) {
264
                                          and not $itemdata->{itemlost};
266
                                $modified++;
265
                                    }
267
                                $item->$c($value);
266
                                };
267
                                push @$upd_biblionumbers, $itemdata->{'biblionumber'};
268
                            }
268
                            }
269
                        }
269
                        }
270
271
                        $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?
272
                        if ( $modified) {
273
                            my $itemlost_pre = $item->itemlost;
274
                            $item->set($new_item_data)->store({skip_record_index => 1});
275
276
                            push @$upd_biblionumbers, $itemdata->{'biblionumber'};
277
278
                            LostItem(
279
                                $item->itemnumber, 'batchmod', undef,
280
                                { skip_record_index => 1 }
281
                            ) if $item->itemlost
282
                                  and not $itemlost_pre;
283
                        }
284
270
                        $modified_items++ if $modified || $modified_holds_priority;
285
                        $modified_items++ if $modified || $modified_holds_priority;
271
                        $modified_fields += $modified + $modified_holds_priority;
286
                        $modified_fields += $modified + $modified_holds_priority;
272
                    }
287
                    }
273
                    $i++;
274
                }
275
                if (@not_deleted) {
276
                    Koha::Exceptions::Exception->throw(
277
                        'Some items have not been deleted, rolling back');
278
                }
288
                }
279
            }
289
            );
280
        );
281
    }
282
    catch {
283
        if ( $_->isa('Koha::Exceptions::Exception') ) {
284
            $template->param( deletion_failed => 1 );
285
        }
290
        }
286
        die "Something terrible has happened!"
291
        catch {
287
            if ($_ =~ /Rollback failed/); # Rollback failed
292
            warn $_;
288
    };
293
            die "Something terrible has happened!"
294
                if ($_ =~ /Rollback failed/); # Rollback failed
295
        };
296
    }
297
289
    $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once
298
    $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once
290
299
291
    # Don't send specialUpdate for records we are going to delete
300
    # Don't send specialUpdate for records we are going to delete
Lines 320-325 if ($op eq "action") { Link Here
320
        $template->param( "job_completed" => 1 );
329
        $template->param( "job_completed" => 1 );
321
    }
330
    }
322
331
332
323
    # Calling the template
333
    # Calling the template
324
    $template->param(
334
    $template->param(
325
        modified_items => $modified_items,
335
        modified_items => $modified_items,
Lines 588-590 sub UpdateMarcWith { Link Here
588
    }
598
    }
589
    return $modified;
599
    return $modified;
590
}
600
}
591
- 
601
602
sub apply_regex {
603
    my ($params) = @_;
604
    my $search   = $params->{search};
605
    my $replace  = $params->{replace};
606
    my $modifiers = $params->{modifiers} || [];
607
    my $value = $params->{value};
608
609
    my @available_modifiers = qw( i g );
610
    my $retained_modifiers  = q||;
611
    for my $modifier ( split //, @$modifiers ) {
612
        $retained_modifiers .= $modifier
613
          if grep { /$modifier/ } @available_modifiers;
614
    }
615
    if ( $retained_modifiers =~ m/^(ig|gi)$/ ) {
616
        $value =~ s/$search/$replace/ig;
617
    }
618
    elsif ( $retained_modifiers eq 'i' ) {
619
        $value =~ s/$search/$replace/i;
620
    }
621
    elsif ( $retained_modifiers eq 'g' ) {
622
        $value =~ s/$search/$replace/g;
623
    }
624
    else {
625
        $value =~ s/$search/$replace/;
626
    }
627
628
    return $value;
629
}

Return to bug 28445