1 package C4::MarcModificationTemplates;
3 # This file is part of Koha.
5 # Copyright 2010 Kyle M Hall <kyle.m.hall@gmail.com>
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
25 use Koha::SimpleMARC qw(
27 copy_and_replace_field
36 use Koha::DateUtils qw( dt_from_string );
38 use vars qw(@ISA @EXPORT);
43 GetModificationTemplates
44 AddModificationTemplate
45 DelModificationTemplate
47 GetModificationTemplateAction
48 GetModificationTemplateActions
50 AddModificationTemplateAction
51 ModModificationTemplateAction
52 DelModificationTemplateAction
53 MoveModificationTemplateAction
55 ModifyRecordsWithTemplate
56 ModifyRecordWithTemplate
63 C4::MarcModificationTemplates - Module to manage MARC Modification Templates
67 MARC Modification Templates are a tool for marc batch imports,
68 so that librarians can set up templates for various vendors'
69 files telling Koha what fields to insert data into.
75 =head2 GetModificationTemplates
77 my @templates = GetModificationTemplates( $template_id );
79 Passing optional $template_id marks it as the selected template.
83 sub GetModificationTemplates {
84 my ( $template_id ) = @_;
86 my $dbh = C4::Context->dbh;
87 my $sth = $dbh->prepare("SELECT * FROM marc_modification_templates ORDER BY name");
91 while ( my $template = $sth->fetchrow_hashref() ) {
92 $template->{'selected'} = 1
93 if $template_id && $template->{'template_id'} eq $template_id;
94 push( @templates, $template );
101 AddModificationTemplate
103 $template_id = AddModificationTemplate( $template_name[, $template_id ] );
105 If $template_id is supplied, the actions from that template will be copied
106 into the newly created template.
109 sub AddModificationTemplate {
110 my ( $template_name, $template_id_copy ) = @_;
112 my $dbh = C4::Context->dbh;
113 my $sth = $dbh->prepare("INSERT INTO marc_modification_templates ( name ) VALUES ( ? )");
114 $sth->execute( $template_name );
116 $sth = $dbh->prepare("SELECT * FROM marc_modification_templates WHERE name = ?");
117 $sth->execute( $template_name );
118 my $row = $sth->fetchrow_hashref();
119 my $template_id = $row->{'template_id'};
121 if ( $template_id_copy ) {
122 my @actions = GetModificationTemplateActions( $template_id_copy );
123 foreach my $action ( @actions ) {
124 AddModificationTemplateAction(
127 $action->{'field_number'},
128 $action->{'from_field'},
129 $action->{'from_subfield'},
130 $action->{'field_value'},
131 $action->{'to_field'},
132 $action->{'to_subfield'},
133 $action->{'to_regex_search'},
134 $action->{'to_regex_replace'},
135 $action->{'to_regex_modifiers'},
136 $action->{'conditional'},
137 $action->{'conditional_field'},
138 $action->{'conditional_subfield'},
139 $action->{'conditional_comparison'},
140 $action->{'conditional_value'},
141 $action->{'conditional_regex'},
142 $action->{'description'},
152 DelModificationTemplate
154 DelModificationTemplate( $template_id );
157 sub DelModificationTemplate {
158 my ( $template_id ) = @_;
160 my $dbh = C4::Context->dbh;
161 my $sth = $dbh->prepare("DELETE FROM marc_modification_templates WHERE template_id = ?");
162 $sth->execute( $template_id );
166 GetModificationTemplateAction
168 my $action = GetModificationTemplateAction( $mmta_id );
171 sub GetModificationTemplateAction {
172 my ( $mmta_id ) = @_;
174 my $dbh = C4::Context->dbh;
175 my $sth = $dbh->prepare("SELECT * FROM marc_modification_template_actions WHERE mmta_id = ?");
176 $sth->execute( $mmta_id );
177 my $action = $sth->fetchrow_hashref();
183 GetModificationTemplateActions
185 my @actions = GetModificationTemplateActions( $template_id );
188 sub GetModificationTemplateActions {
189 my ( $template_id ) = @_;
191 my $dbh = C4::Context->dbh;
192 my $sth = $dbh->prepare("SELECT * FROM marc_modification_template_actions WHERE template_id = ? ORDER BY ordering");
193 $sth->execute( $template_id );
196 while ( my $action = $sth->fetchrow_hashref() ) {
197 push( @actions, $action );
204 AddModificationTemplateAction
206 AddModificationTemplateAction(
207 $template_id, $action, $field_number,
208 $from_field, $from_subfield, $field_value,
209 $to_field, $to_subfield, $to_regex_search, $to_regex_replace, $to_regex_modifiers
210 $conditional, $conditional_field, $conditional_subfield,
211 $conditional_comparison, $conditional_value,
212 $conditional_regex, $description
215 Adds a new action to the given modification template.
219 sub AddModificationTemplateAction {
234 $conditional_subfield,
235 $conditional_comparison,
241 $conditional ||= undef;
242 $conditional_comparison ||= undef;
243 $conditional_regex ||= '0';
245 my $dbh = C4::Context->dbh;
246 my $sth = $dbh->prepare( 'SELECT MAX(ordering) + 1 AS next_ordering FROM marc_modification_template_actions WHERE template_id = ?' );
247 $sth->execute( $template_id );
248 my $row = $sth->fetchrow_hashref;
249 my $ordering = $row->{'next_ordering'} || 1;
252 INSERT INTO marc_modification_template_actions (
268 conditional_subfield,
269 conditional_comparison,
274 VALUES ( NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
276 $sth = $dbh->prepare( $query );
293 $conditional_subfield,
294 $conditional_comparison,
302 ModModificationTemplateAction
304 ModModificationTemplateAction(
305 $mmta_id, $action, $field_number, $from_field,
306 $from_subfield, $field_value, $to_field,
307 $to_subfield, $to_regex_search, $to_regex_replace, $to_regex_modifiers, $conditional,
308 $conditional_field, $conditional_subfield,
309 $conditional_comparison, $conditional_value,
310 $conditional_regex, $description
313 Modifies an existing action.
317 sub ModModificationTemplateAction {
332 $conditional_subfield,
333 $conditional_comparison,
339 my $dbh = C4::Context->dbh;
340 $conditional ||= undef;
341 $conditional_comparison ||= undef;
342 $conditional_regex ||= '0';
345 UPDATE marc_modification_template_actions SET
354 to_regex_replace = ?,
355 to_regex_modifiers = ?,
357 conditional_field = ?,
358 conditional_subfield = ?,
359 conditional_comparison = ?,
360 conditional_value = ?,
361 conditional_regex = ?,
365 my $sth = $dbh->prepare( $query );
380 $conditional_subfield,
381 $conditional_comparison,
391 DelModificationTemplateAction
393 DelModificationTemplateAction( $mmta_id );
395 Deletes the given template action.
398 sub DelModificationTemplateAction {
399 my ( $mmta_id ) = @_;
401 my $action = GetModificationTemplateAction( $mmta_id );
403 my $dbh = C4::Context->dbh;
404 my $sth = $dbh->prepare("DELETE FROM marc_modification_template_actions WHERE mmta_id = ?");
405 $sth->execute( $mmta_id );
407 $sth = $dbh->prepare("UPDATE marc_modification_template_actions SET ordering = ordering - 1 WHERE template_id = ? AND ordering > ?");
408 $sth->execute( $action->{'template_id'}, $action->{'ordering'} );
412 MoveModificationTemplateAction
414 MoveModificationTemplateAction( $mmta_id, $where );
416 Changes the order for the given action.
417 Options for $where are 'up', 'down', 'top' and 'bottom'
419 sub MoveModificationTemplateAction {
420 my ( $mmta_id, $where ) = @_;
422 my $action = GetModificationTemplateAction( $mmta_id );
424 return if ( $action->{'ordering'} eq '1' && ( $where eq 'up' || $where eq 'top' ) );
425 return if ( $action->{'ordering'} eq GetModificationTemplateActions( $action->{'template_id'} ) && ( $where eq 'down' || $where eq 'bottom' ) );
427 my $dbh = C4::Context->dbh;
430 if ( $where eq 'up' || $where eq 'down' ) {
432 ## For up and down, we just swap the ordering number with the one above or below it.
434 ## Change the ordering for the other action
435 $query = "UPDATE marc_modification_template_actions SET ordering = ? WHERE template_id = ? AND ordering = ?";
437 my $ordering = $action->{'ordering'};
438 $ordering-- if ( $where eq 'up' );
439 $ordering++ if ( $where eq 'down' );
441 $sth = $dbh->prepare( $query );
442 $sth->execute( $action->{'ordering'}, $action->{'template_id'}, $ordering );
444 ## Change the ordering for this action
445 $query = "UPDATE marc_modification_template_actions SET ordering = ? WHERE mmta_id = ?";
446 $sth = $dbh->prepare( $query );
447 $sth->execute( $ordering, $action->{'mmta_id'} );
449 } elsif ( $where eq 'top' ) {
451 $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET ordering = ordering + 1 WHERE template_id = ? AND ordering < ?');
452 $sth->execute( $action->{'template_id'}, $action->{'ordering'} );
454 $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET ordering = 1 WHERE mmta_id = ?');
455 $sth->execute( $mmta_id );
457 } elsif ( $where eq 'bottom' ) {
459 my $ordering = GetModificationTemplateActions( $action->{'template_id'} );
461 $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET ordering = ordering - 1 WHERE template_id = ? AND ordering > ?');
462 $sth->execute( $action->{'template_id'}, $action->{'ordering'} );
464 $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET ordering = ? WHERE mmta_id = ?');
465 $sth->execute( $ordering, $mmta_id );
472 ModifyRecordsWithTemplate
474 ModifyRecordsWithTemplate( $template_id, $batch );
476 Accepts a template id and a MARC::Batch object.
479 sub ModifyRecordsWithTemplate {
480 my ( $template_id, $batch ) = @_;
482 while ( my $record = $batch->next() ) {
483 ModifyRecordWithTemplate( $template_id, $record );
488 ModifyRecordWithTemplate
490 ModifyRecordWithTemplate( $template_id, $record )
492 Accepts a MARC::Record object ( $record ) and modifies
493 it based on the actions for the given $template_id
496 sub ModifyRecordWithTemplate {
497 my ( $template_id, $record ) = @_;
499 my $current_date = dt_from_string()->ymd();
501 $branchcode = C4::Context->userenv->{branch} if C4::Context->userenv;
503 my @actions = GetModificationTemplateActions( $template_id );
505 foreach my $a ( @actions ) {
506 my $action = $a->{'action'};
507 my $field_number = $a->{'field_number'} // 1;
508 my $from_field = $a->{'from_field'};
509 my $from_subfield = $a->{'from_subfield'};
510 my $field_value = $a->{'field_value'};
511 my $to_field = $a->{'to_field'};
512 my $to_subfield = $a->{'to_subfield'};
513 my $to_regex_search = $a->{'to_regex_search'};
514 my $to_regex_replace = $a->{'to_regex_replace'};
515 my $to_regex_modifiers = $a->{'to_regex_modifiers'};
516 my $conditional = $a->{'conditional'};
517 my $conditional_field = $a->{'conditional_field'};
518 my $conditional_subfield = $a->{'conditional_subfield'};
519 my $conditional_comparison = $a->{'conditional_comparison'};
520 my $conditional_value = $a->{'conditional_value'};
521 my $conditional_regex = $a->{'conditional_regex'};
523 if ( $field_value ) {
524 $field_value =~ s/__CURRENTDATE__/$current_date/g;
525 $field_value =~ s/__BRANCHCODE__/$branchcode/g;
529 my $field_numbers = [];
530 if ( $conditional ) {
531 if ( $conditional_comparison eq 'exists' ) {
532 $field_numbers = field_exists({
534 field => $conditional_field,
535 subfield => $conditional_subfield,
537 $do = $conditional eq 'if'
539 : not @$field_numbers;
541 elsif ( $conditional_comparison eq 'not_exists' ) {
542 $field_numbers = field_exists({
544 field => $conditional_field,
545 subfield => $conditional_subfield
547 $do = $conditional eq 'if'
548 ? not @$field_numbers
551 elsif ( $conditional_comparison eq 'equals' ) {
552 $field_numbers = field_equals({
554 value => $conditional_value,
555 field => $conditional_field,
556 subfield => $conditional_subfield,
557 is_regex => $conditional_regex,
559 $do = $conditional eq 'if'
561 : not @$field_numbers;
563 elsif ( $conditional_comparison eq 'not_equals' ) {
564 $field_numbers = field_equals({
566 value => $conditional_value,
567 field => $conditional_field,
568 subfield => $conditional_subfield,
569 is_regex => $conditional_regex,
576 field => $conditional_field,
577 subfield => $conditional_subfield
582 $field_numbers = [Koha::MoreUtils::singleton ( @$field_numbers, @$all_fields ) ];
583 if ( $from_field == $conditional_field ){
584 $do = $conditional eq 'if'
586 : not @$field_numbers;
588 $do = $conditional eq 'if'
589 ? not @$field_numbers
597 # field_number == 0 if all field need to be updated
598 # or 1 if only the first field need to be updated
600 # A condition has been given
601 if ( @$field_numbers > 0 ) {
602 if ( $field_number == 1 ) {
603 # We want only the first
604 if ( $from_field == $conditional_field ){
605 # want first field matching condition
606 $field_numbers = [ $field_numbers->[0] ];
608 # condition doesn't match, so just want first occurrence of from field
609 $field_numbers = [ 1 ];
612 unless ( $from_field == $conditional_field ){
613 # condition doesn't match from fields so need all occurrences of from fields for action
614 $field_numbers = field_exists({
616 field => $from_field,
617 subfield => $from_subfield,
622 # There was no condition
624 if ( $field_number == 1 ) {
625 # We want to process the first field
626 $field_numbers = [ 1 ];
630 if ( $action eq 'copy_field' ) {
633 from_field => $from_field,
634 from_subfield => $from_subfield,
635 to_field => $to_field,
636 to_subfield => $to_subfield,
638 search => $to_regex_search,
639 replace => $to_regex_replace,
640 modifiers => $to_regex_modifiers
642 field_numbers => $field_numbers,
645 elsif ( $action eq 'copy_and_replace_field' ) {
646 copy_and_replace_field({
648 from_field => $from_field,
649 from_subfield => $from_subfield,
650 to_field => $to_field,
651 to_subfield => $to_subfield,
653 search => $to_regex_search,
654 replace => $to_regex_replace,
655 modifiers => $to_regex_modifiers
657 field_numbers => $field_numbers,
660 elsif ( $action eq 'add_field' ) {
663 field => $from_field,
664 subfield => $from_subfield,
665 values => [ $field_value ],
666 field_numbers => $field_numbers,
669 elsif ( $action eq 'update_field' ) {
672 field => $from_field,
673 subfield => $from_subfield,
674 values => [ $field_value ],
675 field_numbers => $field_numbers,
678 elsif ( $action eq 'move_field' ) {
681 from_field => $from_field,
682 from_subfield => $from_subfield,
683 to_field => $to_field,
684 to_subfield => $to_subfield,
686 search => $to_regex_search,
687 replace => $to_regex_replace,
688 modifiers => $to_regex_modifiers
690 field_numbers => $field_numbers,
693 elsif ( $action eq 'delete_field' ) {
696 field => $from_field,
697 subfield => $from_subfield,
698 field_numbers => $field_numbers,