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

(-)a/C4/Biblio.pm (-19 / +24 lines)
Lines 59-65 BEGIN { Link Here
59
        DelBiblio
59
        DelBiblio
60
        BiblioAutoLink
60
        BiblioAutoLink
61
        LinkBibHeadingsToAuthorities
61
        LinkBibHeadingsToAuthorities
62
        ApplyMarcMergeRules
62
        ApplyMarcOverlayRules
63
        TransformMarcToKoha
63
        TransformMarcToKoha
64
        TransformHtmlToMarc
64
        TransformHtmlToMarc
65
        TransformHtmlToXml
65
        TransformHtmlToXml
Lines 101-112 use Koha::Acquisition::Currencies; Link Here
101
use Koha::Biblio::Metadatas;
101
use Koha::Biblio::Metadatas;
102
use Koha::Holds;
102
use Koha::Holds;
103
use Koha::ItemTypes;
103
use Koha::ItemTypes;
104
use Koha::MarcOverlayRules;
104
use Koha::Plugins;
105
use Koha::Plugins;
105
use Koha::SearchEngine;
106
use Koha::SearchEngine;
106
use Koha::SearchEngine::Indexer;
107
use Koha::SearchEngine::Indexer;
107
use Koha::Libraries;
108
use Koha::Libraries;
108
use Koha::Util::MARC;
109
use Koha::Util::MARC;
109
use Koha::MarcMergeRules;
110
110
111
use vars qw($debug $cgi_debug);
111
use vars qw($debug $cgi_debug);
112
112
Lines 327-335 The C<$options> argument is a hashref with additional parameters: Link Here
327
327
328
=item C<context>
328
=item C<context>
329
329
330
This parameter is forwared to L</ApplyMarcMergeRules> where it is used for
330
This parameter is forwarded to L</ApplyMarcOverlayRules> where it is used for
331
selecting the current rule set if Marc Merge Rules is enabled.
331
selecting the current rule set if MARCOverlayRules is enabled.
332
See L</ApplyMarcMergeRules> for more details.
332
See L</ApplyMarcOverlayRules> for more details.
333
333
334
=item C<disable_autolink>
334
=item C<disable_autolink>
335
335
Lines 378-389 sub ModBiblio { Link Here
378
378
379
    _strip_item_fields($record, $frameworkcode);
379
    _strip_item_fields($record, $frameworkcode);
380
380
381
    # apply merge rules
381
    # apply overlay rules
382
    if (C4::Context->preference('MARCMergeRules') && $biblionumber && defined $options && exists $options->{'context'}) {
382
    if (   C4::Context->preference('MARCOverlayRules')
383
        $record = ApplyMarcMergeRules({
383
        && $biblionumber
384
        && defined $options
385
        && exists $options->{'context'} )
386
    {
387
        $record = ApplyMarcOverlayRules(
388
            {
384
                biblionumber => $biblionumber,
389
                biblionumber => $biblionumber,
385
                record => $record,
390
                record       => $record,
386
                context => $options->{'context'},
391
                context      => $options->{'context'},
387
            }
392
            }
388
        );
393
        );
389
    }
394
    }
Lines 3243-3251 sub RemoveAllNsb { Link Here
3243
    return $record;
3248
    return $record;
3244
}
3249
}
3245
3250
3246
=head2 ApplyMarcMergeRules
3251
=head2 ApplyMarcOverlayRules
3247
3252
3248
    my $record = ApplyMarcMergeRules($params)
3253
    my $record = ApplyMarcOverlayRules($params)
3249
3254
3250
Applies marc merge rules to a record.
3255
Applies marc merge rules to a record.
3251
3256
Lines 3281-3310 fields with this field tag from C<record>. Link Here
3281
3286
3282
=cut
3287
=cut
3283
3288
3284
sub ApplyMarcMergeRules {
3289
sub ApplyMarcOverlayRules {
3285
    my ($params) = @_;
3290
    my ($params) = @_;
3286
    my $biblionumber = $params->{biblionumber};
3291
    my $biblionumber = $params->{biblionumber};
3287
    my $incoming_record = $params->{record};
3292
    my $incoming_record = $params->{record};
3288
3293
3289
    if (!$biblionumber) {
3294
    if (!$biblionumber) {
3290
        carp 'ApplyMarcMergeRules called on undefined biblionumber';
3295
        carp 'ApplyMarcOverlayRules called on undefined biblionumber';
3291
        return;
3296
        return;
3292
    }
3297
    }
3293
    if (!$incoming_record) {
3298
    if (!$incoming_record) {
3294
        carp 'ApplyMarcMergeRules called on undefined record';
3299
        carp 'ApplyMarcOverlayRules called on undefined record';
3295
        return;
3300
        return;
3296
    }
3301
    }
3297
    my $old_record = GetMarcBiblio({ biblionumber => $biblionumber });
3302
    my $old_record = GetMarcBiblio({ biblionumber => $biblionumber });
3298
3303
3299
    # Skip merge rules if called with no context
3304
    # Skip overlay rules if called with no context
3300
    if ($old_record && defined $params->{context}) {
3305
    if ($old_record && defined $params->{context}) {
3301
        return Koha::MarcMergeRules->merge_records($old_record, $incoming_record, $params->{context});
3306
        return Koha::MarcOverlayRules->merge_records($old_record, $incoming_record, $params->{context});
3302
    }
3307
    }
3303
    return $incoming_record;
3308
    return $incoming_record;
3304
}
3309
}
3305
3310
3306
1;
3307
3308
=head2 _after_biblio_action_hooks
3311
=head2 _after_biblio_action_hooks
3309
3312
3310
Helper method that takes care of calling all plugin hooks
3313
Helper method that takes care of calling all plugin hooks
Lines 3328-3333 sub _after_biblio_action_hooks { Link Here
3328
    );
3331
    );
3329
}
3332
}
3330
3333
3334
1;
3335
3331
__END__
3336
__END__
3332
3337
3333
=head1 AUTHOR
3338
=head1 AUTHOR
(-)a/Koha/Exceptions/MarcMergeRule.pm (-11 / +11 lines)
Lines 1-4 Link Here
1
package Koha::Exceptions::MarcMergeRule;
1
package Koha::Exceptions::MarcOverlayRule;
2
2
3
# This file is part of Koha.
3
# This file is part of Koha.
4
#
4
#
Lines 19-52 use Modern::Perl; Link Here
19
19
20
use Exception::Class (
20
use Exception::Class (
21
21
22
    'Koha::Exceptions::MarcMergeRule' => {
22
    'Koha::Exceptions::MarcOverlayRule' => {
23
        description => 'Something went wrong!',
23
        description => 'Something went wrong!',
24
    },
24
    },
25
    'Koha::Exceptions::MarcMergeRule::InvalidTagRegExp' => {
25
    'Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp' => {
26
        isa => 'Koha::Exceptions::MarcMergeRule',
26
        isa => 'Koha::Exceptions::MarcOverlayRule',
27
        description => 'Invalid regular expression for tag'
27
        description => 'Invalid regular expression for tag'
28
    },
28
    },
29
    'Koha::Exceptions::MarcMergeRule::InvalidControlFieldActions' => {
29
    'Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions' => {
30
        isa => 'Koha::Exceptions::MarcMergeRule',
30
        isa => 'Koha::Exceptions::MarcOverlayRule',
31
        description => 'Invalid control field actions'
31
        description => 'Invalid control field actions'
32
    }
32
    }
33
);
33
);
34
34
35
=head1 NAME
35
=head1 NAME
36
36
37
Koha::Exceptions::MarcMergeRule - Base class for MarcMergeRule exceptions
37
Koha::Exceptions::MarcOverlayRule - Base class for MarcOverlayRule exceptions
38
38
39
=head1 Exceptions
39
=head1 Exceptions
40
40
41
=head2 Koha::Exceptions::MarcMergeRule
41
=head2 Koha::Exceptions::MarcOverlayRule
42
42
43
Generic MarcMergeRule exception
43
Generic MarcOverlayRule exception
44
44
45
=head2 Koha::Exceptions::MarcMergeRule::InvalidTagRegExp
45
=head2 Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp
46
46
47
Exception for rule validation when rule tag is an invalid regular expression
47
Exception for rule validation when rule tag is an invalid regular expression
48
48
49
=head2 Koha::Exceptions::MarcMergeRule::InvalidControlFieldActions
49
=head2 Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions
50
50
51
Exception for rule validation for control field rules with invalid combination of actions
51
Exception for rule validation for control field rules with invalid combination of actions
52
52
(-)a/Koha/MarcMergeRule.pm (-5 / +5 lines)
Lines 1-4 Link Here
1
package Koha::MarcMergeRule;
1
package Koha::MarcOverlayRule;
2
2
3
# This file is part of Koha.
3
# This file is part of Koha.
4
#
4
#
Lines 23-29 my $cache = Koha::Caches->get_instance(); Link Here
23
23
24
=head1 NAME
24
=head1 NAME
25
25
26
Koha::MarcMergeRule - Koha MarcMergeRule Object class
26
Koha::MarcOverlayRule - Koha MarcOverlayRule Object class
27
27
28
=cut
28
=cut
29
29
Lines 35-41 Override C<store> to clear marc merge rules cache. Link Here
35
35
36
sub store {
36
sub store {
37
    my $self = shift @_;
37
    my $self = shift @_;
38
    $cache->clear_from_cache('marc_merge_rules');
38
    $cache->clear_from_cache('marc_overlay_rules');
39
    $self->SUPER::store(@_);
39
    $self->SUPER::store(@_);
40
}
40
}
41
41
Lines 47-58 Override C<delete> to clear marc merge rules cache. Link Here
47
47
48
sub delete {
48
sub delete {
49
    my $self = shift @_;
49
    my $self = shift @_;
50
    $cache->clear_from_cache('marc_merge_rules');
50
    $cache->clear_from_cache('marc_overlay_rules');
51
    $self->SUPER::delete(@_);
51
    $self->SUPER::delete(@_);
52
}
52
}
53
53
54
sub _type {
54
sub _type {
55
    return 'MarcMergeRule';
55
    return 'MarcOverlayRule';
56
}
56
}
57
57
58
1;
58
1;
(-)a/Koha/MarcMergeRules.pm (-22 / +22 lines)
Lines 1-4 Link Here
1
package Koha::MarcMergeRules;
1
package Koha::MarcOverlayRules;
2
2
3
# This file is part of Koha.
3
# This file is part of Koha.
4
#
4
#
Lines 17-26 package Koha::MarcMergeRules; Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
use List::Util qw(first);
19
use List::Util qw(first);
20
use Koha::MarcMergeRule;
20
use Koha::MarcOverlayRule;
21
use Carp;
21
use Carp;
22
22
23
use Koha::Exceptions::MarcMergeRule;
23
use Koha::Exceptions::MarcOverlayRule;
24
use Try::Tiny;
24
use Try::Tiny;
25
use Scalar::Util qw(looks_like_number);
25
use Scalar::Util qw(looks_like_number);
26
26
Lines 30-40 my $cache = Koha::Caches->get_instance(); Link Here
30
30
31
=head1 NAME
31
=head1 NAME
32
32
33
Koha::MarcMergeRules - Koha MarcMergeRules Object set class
33
Koha::MarcOverlayRules - Koha MarcOverlayRules Object set class
34
34
35
=head1 API
35
=head1 API
36
36
37
=head2 Class Methods
37
=head2 Class methods
38
38
39
=head3 operations
39
=head3 operations
40
40
Lines 48-56 sub operations { Link Here
48
48
49
=head3 context_rules
49
=head3 context_rules
50
50
51
    my $rules = Koha::MarcMergeRules->context_rules($context);
51
    my $rules = Koha::MarcOverlayRules->context_rules($context);
52
52
53
Gets all MARC merge rules for the supplied C<$context> (hashref with { module => filter, ... } values).
53
Gets all MARC overlay rules for the supplied C<$context> (hashref with { module => filter, ... } values).
54
54
55
=cut
55
=cut
56
56
Lines 59-65 sub context_rules { Link Here
59
59
60
    return unless %{$context};
60
    return unless %{$context};
61
61
62
    my $rules = $cache->get_from_cache('marc_merge_rules', { unsafe => 1 });
62
    my $rules = $cache->get_from_cache('marc_overlay_rules', { unsafe => 1 });
63
63
64
    if (!$rules) {
64
    if (!$rules) {
65
        $rules = {};
65
        $rules = {};
Lines 93-99 sub context_rules { Link Here
93
                push @{$regexps}, [$rule{tag}, $operations];
93
                push @{$regexps}, [$rule{tag}, $operations];
94
            }
94
            }
95
        }
95
        }
96
        $cache->set_in_cache('marc_merge_rules', $rules);
96
        $cache->set_in_cache('marc_overlay_rules', $rules);
97
    }
97
    }
98
98
99
    my $context_rules = undef;
99
    my $context_rules = undef;
Lines 120-128 sub context_rules { Link Here
120
120
121
=head3 merge_records
121
=head3 merge_records
122
122
123
    my $merged_record = Koha::MarcMergeRules->merge_records($old_record, $incoming_record, $context);
123
    my $merged_record = Koha::MarcOverlayRules->merge_records($old_record, $incoming_record, $context);
124
124
125
Merge C<$old_record> with C<$incoming_record> applying merge rules for C<$context>.
125
Overlay C<$old_record> with C<$incoming_record> applying overlay rules for C<$context>.
126
Returns merged record C<$merged_record>. C<$old_record>, C<$incoming_record> and
126
Returns merged record C<$merged_record>. C<$old_record>, C<$incoming_record> and
127
C<$merged_record> are all MARC::Record objects.
127
C<$merged_record> are all MARC::Record objects.
128
128
Lines 289-300 sub merge_records { Link Here
289
}
289
}
290
290
291
sub _clear_caches {
291
sub _clear_caches {
292
    $cache->clear_from_cache('marc_merge_rules');
292
    $cache->clear_from_cache('marc_overlay_rules');
293
}
293
}
294
294
295
=head2 find_or_create
295
=head2 find_or_create
296
296
297
Override C<find_or_create> to clear marc merge rules cache.
297
Override C<find_or_create> to clear marc overlay rules cache.
298
298
299
=cut
299
=cut
300
300
Lines 306-312 sub find_or_create { Link Here
306
306
307
=head2 update
307
=head2 update
308
308
309
Override C<update> to clear marc merge rules cache.
309
Override C<update> to clear marc overlay rules cache.
310
310
311
=cut
311
=cut
312
312
Lines 318-324 sub update { Link Here
318
318
319
=head2 delete
319
=head2 delete
320
320
321
Override C<delete> to clear marc merge rules cache.
321
Override C<delete> to clear marc overlay rules cache.
322
322
323
=cut
323
=cut
324
324
Lines 330-340 sub delete { Link Here
330
330
331
=head2 validate
331
=head2 validate
332
332
333
    Koha::MarcMergeRules->validate($rule_data);
333
    Koha::MarcOverlayRules->validate($rule_data);
334
334
335
Validates C<$rule_data>. Throws C<Koha::Exceptions::MarcMergeRule::InvalidTagRegExp>
335
Validates C<$rule_data>. Throws C<Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp>
336
if C<$rule_data->{tag}> contains an invalid regular expression. Throws
336
if C<$rule_data->{tag}> contains an invalid regular expression. Throws
337
C<Koha::Exceptions::MarcMergeRule::InvalidControlFieldActions> if contains invalid
337
C<Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions> if contains invalid
338
combination of actions for control fields. Otherwise returns true.
338
combination of actions for control fields. Otherwise returns true.
339
339
340
=cut
340
=cut
Lines 346-352 sub validate { Link Here
346
        if ($rule_data->{tag} ne '*') {
346
        if ($rule_data->{tag} ne '*') {
347
            eval { qr/$rule_data->{tag}/ };
347
            eval { qr/$rule_data->{tag}/ };
348
            if ($@) {
348
            if ($@) {
349
                Koha::Exceptions::MarcMergeRule::InvalidTagRegExp->throw(
349
                Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp->throw(
350
                    "Invalid tag regular expression"
350
                    "Invalid tag regular expression"
351
                );
351
                );
352
            }
352
            }
Lines 358-364 sub validate { Link Here
358
            $rule_data->{append} &&
358
            $rule_data->{append} &&
359
            !$rule_data->{remove}
359
            !$rule_data->{remove}
360
        ) {
360
        ) {
361
            Koha::Exceptions::MarcMergeRule::InvalidControlFieldActions->throw(
361
            Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions->throw(
362
                "Combination of allow append and skip remove not permitted for control fields"
362
                "Combination of allow append and skip remove not permitted for control fields"
363
            );
363
            );
364
        }
364
        }
Lines 367-373 sub validate { Link Here
367
}
367
}
368
368
369
sub _type {
369
sub _type {
370
    return 'MarcMergeRule';
370
    return 'MarcOverlayRule';
371
}
371
}
372
372
373
=head3 object_class
373
=head3 object_class
Lines 375-381 sub _type { Link Here
375
=cut
375
=cut
376
376
377
sub object_class {
377
sub object_class {
378
    return 'Koha::MarcMergeRule';
378
    return 'Koha::MarcOverlayRule';
379
}
379
}
380
380
381
1;
381
1;
(-)a/admin/marc-merge-rules.pl (-12 / +11 lines)
Lines 34-44 use C4::ImportBatch; Link Here
34
use C4::Matcher;
34
use C4::Matcher;
35
use C4::BackgroundJob;
35
use C4::BackgroundJob;
36
use C4::Labels::Batch;
36
use C4::Labels::Batch;
37
use Koha::MarcMergeRules;
37
use Koha::MarcOverlayRules;
38
use Koha::MarcMergeRule;
38
use Koha::Patron::Categories;
39
use Koha::Patron::Categories; # TODO: Required? Try without use
40
39
41
my $script_name = "/cgi-bin/koha/admin/marc-merge-rules.pl";
40
my $script_name = "/cgi-bin/koha/admin/marc-overlay-rules.pl";
42
41
43
my $input = new CGI;
42
my $input = new CGI;
44
my $op = $input->param('op') || '';
43
my $op = $input->param('op') || '';
Lines 67-77 my $rule_from_cgi = sub { Link Here
67
66
68
my ($template, $loggedinuser, $cookie) = get_template_and_user(
67
my ($template, $loggedinuser, $cookie) = get_template_and_user(
69
    {
68
    {
70
        template_name   => "admin/marc-merge-rules.tt",
69
        template_name   => "admin/marc-overlay-rules.tt",
71
        query           => $input,
70
        query           => $input,
72
        type            => "intranet",
71
        type            => "intranet",
73
        authnotrequired => 0,
72
        authnotrequired => 0,
74
        flagsrequired   => { parameters => 'manage_marc_merge_rules' },
73
        flagsrequired   => { parameters => 'manage_marc_overlay_rules' },
75
        debug           => 1,
74
        debug           => 1,
76
    }
75
    }
77
);
76
);
Lines 83-89 our $sessionID = $cookies{'CGISESSID'}->value; Link Here
83
82
84
my $get_rules = sub {
83
my $get_rules = sub {
85
    # TODO: order?
84
    # TODO: order?
86
    return [map { { $_->get_columns() } } Koha::MarcMergeRules->_resultset->all];
85
    return [map { { $_->get_columns() } } Koha::MarcOverlayRules->_resultset->all];
87
};
86
};
88
my $rules;
87
my $rules;
89
88
Lines 101-107 if ($op eq 'remove' || $op eq 'doremove') { Link Here
101
    elsif ($op eq 'doremove') {
100
    elsif ($op eq 'doremove') {
102
        my @remove_ids = $input->multi_param('batchremove');
101
        my @remove_ids = $input->multi_param('batchremove');
103
        push @remove_ids, scalar $input->param('id') if $input->param('id');
102
        push @remove_ids, scalar $input->param('id') if $input->param('id');
104
        Koha::MarcMergeRules->search({ id => { in => \@remove_ids } })->delete();
103
        Koha::MarcOverlayRules->search({ id => { in => \@remove_ids } })->delete();
105
        $rules = $get_rules->();
104
        $rules = $get_rules->();
106
    }
105
    }
107
}
106
}
Lines 120-138 elsif ($op eq 'doedit' || $op eq 'add') { Link Here
120
    my $rule_data = $rule_from_cgi->($input);
119
    my $rule_data = $rule_from_cgi->($input);
121
    if (!@{$errors}) {
120
    if (!@{$errors}) {
122
        try {
121
        try {
123
            Koha::MarcMergeRules->validate($rule_data);
122
            Koha::MarcOverlayRules->validate($rule_data);
124
        }
123
        }
125
        catch {
124
        catch {
126
            die $_ unless blessed $_ && $_->can('rethrow');
125
            die $_ unless blessed $_ && $_->can('rethrow');
127
126
128
            if ($_->isa('Koha::Exceptions::MarcMergeRule::InvalidTagRegExp')) {
127
            if ($_->isa('Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp')) {
129
                push @{$errors}, {
128
                push @{$errors}, {
130
                    type => 'error',
129
                    type => 'error',
131
                    code => 'invalid_tag_regexp',
130
                    code => 'invalid_tag_regexp',
132
                    tag => $rule_data->{tag},
131
                    tag => $rule_data->{tag},
133
                };
132
                };
134
            }
133
            }
135
            elsif ($_->isa('Koha::Exceptions::MarcMergeRule::InvalidControlFieldActions')) {
134
            elsif ($_->isa('Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions')) {
136
                push @{$errors}, {
135
                push @{$errors}, {
137
                    type => 'error',
136
                    type => 'error',
138
                    code => 'invalid_control_field_actions',
137
                    code => 'invalid_control_field_actions',
Lines 144-150 elsif ($op eq 'doedit' || $op eq 'add') { Link Here
144
            }
143
            }
145
        };
144
        };
146
        if (!@{$errors}) {
145
        if (!@{$errors}) {
147
            my $rule = Koha::MarcMergeRules->find_or_create($rule_data);
146
            my $rule = Koha::MarcOverlayRules->find_or_create($rule_data);
148
            # Need to call set and store here in case we have an update
147
            # Need to call set and store here in case we have an update
149
            $rule->set($rule_data);
148
            $rule->set($rule_data);
150
            $rule->store();
149
            $rule->store();
(-)a/installer/data/mysql/atomicupdate/bug_14957-marc-merge-rules.perl (-27 / +37 lines)
Lines 1-41 Link Here
1
$DBversion = 'XXX'; # will be replaced by the RM
1
$DBversion = 'XXX'; # will be replaced by the RM
2
if( CheckVersion( $DBversion ) ) {
2
if( CheckVersion( $DBversion ) ) {
3
    my $sql = q{
4
      CREATE TABLE IF NOT EXISTS `marc_merge_rules` (
5
        `id` int(11) NOT NULL auto_increment,
6
        `tag` varchar(255) NOT NULL,
7
        `module` varchar(127) NOT NULL,
8
        `filter` varchar(255) NOT NULL,
9
        `add` tinyint NOT NULL,
10
        `append` tinyint NOT NULL,
11
        `remove` tinyint NOT NULL,
12
        `delete` tinyint NOT NULL,
13
        PRIMARY KEY(`id`)
14
      );
15
    };
16
    $dbh->do( $sql );
17
3
18
    $sql = q{
4
    unless (TableExists('marc_overlay_rules_modules')) {
5
        $dbh->do(q{
6
            CREATE TABLE `marc_overlay_rules_modules` (
7
              `name` varchar(127) NOT NULL,
8
              `description` varchar(255),
9
              `specificity` int(11) NOT NULL UNIQUE,
10
              PRIMARY KEY(`name`)
11
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
12
        });
13
    }
14
15
    unless (TableExists('marc_overlay_rules')) {
16
        $dbh->do(q{
17
            CREATE TABLE IF NOT EXISTS `marc_overlay_rules` (
18
              `id` int(11) NOT NULL auto_increment,
19
              `tag` varchar(255) NOT NULL, -- can be regexp, so need > 3 chars
20
              `module` varchar(127) NOT NULL,
21
              `filter` varchar(255) NOT NULL,
22
              `add`    TINYINT(1) NOT NULL DEFAULT 0,
23
              `append` TINYINT(1) NOT NULL DEFAULT 0,
24
              `remove` TINYINT(1) NOT NULL DEFAULT 0,
25
              `delete` TINYINT(1) NOT NULL DEFAULT 0,
26
              PRIMARY KEY(`id`),
27
              CONSTRAINT `marc_overlay_rules_ibfk1` FOREIGN KEY (`module`) REFERENCES `marc_overlay_rules_modules` (`name`) ON DELETE CASCADE ON UPDATE CASCADE
28
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
29
        });
30
    }
31
32
    $dbh->do(q{
19
      INSERT IGNORE INTO systempreferences (`variable`, `value`, `options`, `explanation`, `type`) VALUES (
33
      INSERT IGNORE INTO systempreferences (`variable`, `value`, `options`, `explanation`, `type`) VALUES (
20
        'MARCMergeRules',
34
        'MARCOverlayRules',
21
        '0',
35
        '0',
22
        NULL,
36
        NULL,
23
        'Use the MARC merge rules system to decide what actions to take for each field when modifying records.',
37
        'Use the MARC record overlay rules system to decide what actions to take for each field when modifying records.',
24
        'YesNo'
38
        'YesNo'
25
      );
39
      );
26
    };
40
    });
27
    $dbh->do( $sql );
28
41
29
    $sql = q{
42
    $dbh->do(q{
30
      INSERT IGNORE INTO permissions (module_bit, code, description) VALUES (
43
      INSERT IGNORE INTO permissions (module_bit, code, description) VALUES (
31
        3,
44
        3,
32
        'manage_marc_merge_rules',
45
        'manage_marc_overlay_rules',
33
        'Manage MARC merge rules configuration'
46
        'Manage MARC overlay rules configuration'
34
      );
47
      );
35
    };
48
    });
36
    $dbh->do( $sql );
37
49
38
    # Always end with this (adjust the bug info)
50
    NewVersion( $DBversion, 14957, "Add a way to define overlay rules for incoming MARC records)\n";
39
    SetVersion( $DBversion );
40
    print "Upgrade to $DBversion done (Bug 14957 - Write protecting MARC fields based on source of import)\n";
41
}
51
}
(-)a/installer/data/mysql/kohastructure.sql (-14 / +14 lines)
Lines 3383-3393 CREATE TABLE `marc_matchers` ( Link Here
3383
/*!40101 SET character_set_client = @saved_cs_client */;
3383
/*!40101 SET character_set_client = @saved_cs_client */;
3384
3384
3385
--
3385
--
3386
-- Table structure for table `marc_merge_rules_modules`
3386
-- Table structure for table `marc_overlay_rules_modules`
3387
--
3387
--
3388
3388
3389
DROP TABLE IF EXISTS `marc_merge_rules_modules`;
3389
DROP TABLE IF EXISTS `marc_overlay_rules_modules`;
3390
CREATE TABLE `marc_merge_rules_modules` (
3390
CREATE TABLE `marc_overlay_rules_modules` (
3391
  `name` varchar(127) NOT NULL,
3391
  `name` varchar(127) NOT NULL,
3392
  `description` varchar(255),
3392
  `description` varchar(255),
3393
  `specificity` int(11) NOT NULL UNIQUE,
3393
  `specificity` int(11) NOT NULL UNIQUE,
Lines 3395-3416 CREATE TABLE `marc_merge_rules_modules` ( Link Here
3395
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
3395
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
3396
3396
3397
--
3397
--
3398
-- Table structure for table `marc_merge_rules`
3398
-- Table structure for table `marc_overlay_rules`
3399
--
3399
--
3400
3400
3401
DROP TABLE IF EXISTS `marc_merge_rules`;
3401
DROP TABLE IF EXISTS `marc_overlay_rules`;
3402
CREATE TABLE IF NOT EXISTS `marc_merge_rules` (
3402
CREATE TABLE IF NOT EXISTS `marc_overlay_rules` (
3403
  `id` int(11) NOT NULL auto_increment,
3403
  `id`     int(11) NOT NULL auto_increment,
3404
  `tag` varchar(255) NOT NULL, -- can be regexp, so need > 3 chars
3404
  `tag`    varchar(255) NOT NULL, -- can be regexp, so need > 3 chars
3405
  `module` varchar(127) NOT NULL,
3405
  `module` varchar(127) NOT NULL,
3406
  `filter` varchar(255) NOT NULL,
3406
  `filter` varchar(255) NOT NULL,
3407
  `add` tinyint NOT NULL,
3407
  `add`    TINYINT(1) NOT NULL DEFAULT 0,
3408
  `append` tinyint NOT NULL,
3408
  `append` TINYINT(1) NOT NULL DEFAULT 0,
3409
  `remove` tinyint NOT NULL,
3409
  `remove` TINYINT(1) NOT NULL DEFAULT 0,
3410
  `delete` tinyint NOT NULL,
3410
  `delete` TINYINT(1) NOT NULL DEFAULT 0,
3411
  PRIMARY KEY(`id`),
3411
  PRIMARY KEY(`id`),
3412
  CONSTRAINT `marc_merge_rules_ibfk1` FOREIGN KEY (`module`) REFERENCES `marc_merge_rules_modules` (`name`) ON DELETE CASCADE ON UPDATE CASCADE
3412
  CONSTRAINT `marc_overlay_rules_ibfk1` FOREIGN KEY (`module`) REFERENCES `marc_overlay_rules_modules` (`name`) ON DELETE CASCADE ON UPDATE CASCADE
3413
);
3413
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
3414
3414
3415
--
3415
--
3416
-- Table structure for table `marc_modification_template_actions`
3416
-- Table structure for table `marc_modification_template_actions`
(-)a/installer/data/mysql/mandatory/sysprefs.sql (-1 / +1 lines)
Lines 323-331 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
323
('MarcFieldForModifierName','',NULL,'Where to store the name of the record''s last modifier','Free'),
323
('MarcFieldForModifierName','',NULL,'Where to store the name of the record''s last modifier','Free'),
324
('MarcFieldsToOrder','',NULL,'Set the mapping values for a new order line created from a MARC record in a staged file. In a YAML format.','textarea'),
324
('MarcFieldsToOrder','',NULL,'Set the mapping values for a new order line created from a MARC record in a staged file. In a YAML format.','textarea'),
325
('MarcItemFieldsToOrder','',NULL,'Set the mapping values for new item records created from a MARC record in a staged file. In a YAML format.','textarea'),
325
('MarcItemFieldsToOrder','',NULL,'Set the mapping values for new item records created from a MARC record in a staged file. In a YAML format.','textarea'),
326
('MARCMergeRules','0',NULL,'Use the MARC merge rules system to decide what actions to take for each field when modifying records.','YesNo'),
327
('MarkLostItemsAsReturned','batchmod,moredetail,cronjob,additem,pendingreserves,onpayment','claim_returned|batchmod|moredetail|cronjob|additem|pendingreserves|onpayment','Mark items as returned when flagged as lost','multiple'),
326
('MarkLostItemsAsReturned','batchmod,moredetail,cronjob,additem,pendingreserves,onpayment','claim_returned|batchmod|moredetail|cronjob|additem|pendingreserves|onpayment','Mark items as returned when flagged as lost','multiple'),
328
('MARCOrgCode','OSt','','Define MARC Organization Code for MARC21 records - http://www.loc.gov/marc/organizations/orgshome.html','free'),
327
('MARCOrgCode','OSt','','Define MARC Organization Code for MARC21 records - http://www.loc.gov/marc/organizations/orgshome.html','free'),
328
('MARCOverlayRules','0',NULL,'Use the MARC record overlay rules system to decide what actions to take for each field when modifying records.','YesNo'),
329
('MaxFine',NULL,'','Maximum fine a patron can have for all late returns at one moment. Single item caps are specified in the circulation rules matrix.','Integer'),
329
('MaxFine',NULL,'','Maximum fine a patron can have for all late returns at one moment. Single item caps are specified in the circulation rules matrix.','Integer'),
330
('MaxItemsToDisplayForBatchDel','1000',NULL,'Display up to a given number of items in a single item deletionbatch.','Integer'),
330
('MaxItemsToDisplayForBatchDel','1000',NULL,'Display up to a given number of items in a single item deletionbatch.','Integer'),
331
('MaxItemsToDisplayForBatchMod','1000',NULL,'Display up to a given number of items in a single item modification batch.','Integer'),
331
('MaxItemsToDisplayForBatchMod','1000',NULL,'Display up to a given number of items in a single item modification batch.','Integer'),
(-)a/installer/data/mysql/mandatory/userpermissions.sql (-1 / +1 lines)
Lines 25-31 INSERT INTO permissions (module_bit, code, description) VALUES Link Here
25
   ( 3, 'manage_oai_sets', 'Manage OAI sets'),
25
   ( 3, 'manage_oai_sets', 'Manage OAI sets'),
26
   ( 3, 'manage_item_search_fields', 'Manage item search fields'),
26
   ( 3, 'manage_item_search_fields', 'Manage item search fields'),
27
   ( 3, 'manage_search_engine_config', 'Manage search engine configuration'),
27
   ( 3, 'manage_search_engine_config', 'Manage search engine configuration'),
28
   ( 3, 'manage_marc_merge_rules', 'Manage MARC merge rules configuration'),
28
   ( 3, 'manage_marc_overlay_rules', 'Manage MARC overlay rules configuration'),
29
   ( 3, 'manage_search_targets', 'Manage Z39.50 and SRU server configuration'),
29
   ( 3, 'manage_search_targets', 'Manage Z39.50 and SRU server configuration'),
30
   ( 3, 'manage_didyoumean', 'Manage Did you mean? configuration'),
30
   ( 3, 'manage_didyoumean', 'Manage Did you mean? configuration'),
31
   ( 3, 'manage_column_config', 'Manage column configuration'),
31
   ( 3, 'manage_column_config', 'Manage column configuration'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc (-2 / +2 lines)
Lines 93-100 Link Here
93
            [% IF ( CAN_user_parameters_manage_search_engine_config ) %]
93
            [% IF ( CAN_user_parameters_manage_search_engine_config ) %]
94
                <li><a href="/cgi-bin/koha/admin/searchengine/elasticsearch/mappings.pl">Search engine configuration (Elasticsearch)</a></li>
94
                <li><a href="/cgi-bin/koha/admin/searchengine/elasticsearch/mappings.pl">Search engine configuration (Elasticsearch)</a></li>
95
            [% END %]
95
            [% END %]
96
            [% IF ( CAN_user_parameters_manage_marc_merge_rules ) %]
96
            [% IF ( CAN_user_parameters_manage_marc_overlay_rules ) %]
97
                <li><a href="/cgi-bin/koha/admin/marc-merge-rules.pl">MARC merge rules</a></li>
97
                <li><a href="/cgi-bin/koha/admin/marc-overlay-rules.pl">MARC overlay rules</a></li>
98
            [% END %]
98
            [% END %]
99
        </ul>
99
        </ul>
100
    [% END %]
100
    [% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc (-3 / +3 lines)
Lines 210-218 Link Here
210
            Manage search engine configuration
210
            Manage search engine configuration
211
        </span>
211
        </span>
212
        <span class="permissioncode">([% name | html %])</span>
212
        <span class="permissioncode">([% name | html %])</span>
213
    [%- CASE 'manage_marc_merge_rules' -%]
213
    [%- CASE 'manage_marc_overlay_rules' -%]
214
        <span class="sub_permission manage_marc_merge_rules_subpermission">
214
        <span class="sub_permission manage_marc_overlay_rules_subpermission">
215
          Manage MARC merge rules configuration
215
          Manage MARC overlay rules configuration
216
        </span>
216
        </span>
217
        <span class="permissioncode">([% name | html %])</span>
217
        <span class="permissioncode">([% name | html %])</span>
218
    [%- CASE 'manage_search_targets' -%]
218
    [%- CASE 'manage_search_targets' -%]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt (-2 / +2 lines)
Lines 172-179 Link Here
172
                        <dt><a href="/cgi-bin/koha/admin/searchengine/elasticsearch/mappings.pl">Search engine configuration (Elasticsearch)</a></dt>
172
                        <dt><a href="/cgi-bin/koha/admin/searchengine/elasticsearch/mappings.pl">Search engine configuration (Elasticsearch)</a></dt>
173
                        <dd>Manage indexes, facets, and their mappings to MARC fields and subfields</dd>
173
                        <dd>Manage indexes, facets, and their mappings to MARC fields and subfields</dd>
174
                    [% END %]
174
                    [% END %]
175
                    <dt><a href="/cgi-bin/koha/admin/marc-merge-rules.pl">MARC merge rules</a></dt>
175
                    <dt><a href="/cgi-bin/koha/admin/marc-overlay-rules.pl">MARC overlay rules</a></dt>
176
                    <dd>Managed MARC field merge rules</dd>
176
                    <dd>Managed MARC field overlay rules</dd>
177
                </dl>
177
                </dl>
178
            [% END %]
178
            [% END %]
179
179
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/marc-merge-rules.tt (-35 / +35 lines)
Lines 1-6 Link Here
1
[% USE Koha %]
1
[% USE Koha %]
2
[% INCLUDE 'doc-head-open.inc' %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
<title>Koha &rsaquo; Administration &rsaquo; MARC merge rules</title>
3
<title>Koha &rsaquo; Administration &rsaquo; MARC overlay rules</title>
4
[% INCLUDE 'doc-head-close.inc' %]
4
[% INCLUDE 'doc-head-close.inc' %]
5
[% Asset.css("css/datatables.css") %]
5
[% Asset.css("css/datatables.css") %]
6
[% INCLUDE 'datatables.inc' %]
6
[% INCLUDE 'datatables.inc' %]
Lines 16-35 function doSubmit(op, id) { Link Here
16
    $('<input type="hidden"/>')
16
    $('<input type="hidden"/>')
17
    .attr('name', 'op')
17
    .attr('name', 'op')
18
    .attr('value', op)
18
    .attr('value', op)
19
    .appendTo('#marc-merge-rules-form');
19
    .appendTo('#marc-overlay-rules-form');
20
20
21
    if(id) {
21
    if(id) {
22
        $('<input type="hidden"/>')
22
        $('<input type="hidden"/>')
23
        .attr('name', 'id')
23
        .attr('name', 'id')
24
        .attr('value', id)
24
        .attr('value', id)
25
        .appendTo('#marc-merge-rules-form');
25
        .appendTo('#marc-overlay-rules-form');
26
    }
26
    }
27
27
28
    var valid = true;
28
    var valid = true;
29
    if (op == 'add' || op == 'edit') {
29
    if (op == 'add' || op == 'edit') {
30
        var validate = [
30
        var validate = [
31
            $('#marc-merge-rules-form input[name="filter"]'),
31
            $('#marc-overlay-rules-form input[name="filter"]'),
32
            $('#marc-merge-rules-form input[name="tag"]')
32
            $('#marc-overlay-rules-form input[name="tag"]')
33
        ];
33
        ];
34
        for(var i = 0; i < validate.length; i++) {
34
        for(var i = 0; i < validate.length; i++) {
35
            if (validate[i].length) {
35
            if (validate[i].length) {
Lines 44-50 function doSubmit(op, id) { Link Here
44
    }
44
    }
45
45
46
    if (valid) {
46
    if (valid) {
47
        $('#marc-merge-rules-form').submit();
47
        $('#marc-overlay-rules-form').submit();
48
    }
48
    }
49
49
50
    return valid;
50
    return valid;
Lines 87-93 $(document).ready(function(){ Link Here
87
            });
87
            });
88
    }
88
    }
89
89
90
    $('#marc-merge-rules').dataTable($.extend(true, {}, dataTablesDefaults, {
90
    $('#marc-overlay-rules').dataTable($.extend(true, {}, dataTablesDefaults, {
91
        "aoColumns": [
91
        "aoColumns": [
92
            {"bSearchable": false, "bSortable": false},
92
            {"bSearchable": false, "bSortable": false},
93
            {"sSortDataType": "dom-input"},
93
            {"sSortDataType": "dom-input"},
Lines 104-159 $(document).ready(function(){ Link Here
104
        "pagingType": "simple"
104
        "pagingType": "simple"
105
    }));
105
    }));
106
106
107
    var merge_rules_presets = {};
107
    var overlay_rules_presets = {};
108
    merge_rules_presets[_("Protect")] = {
108
    overlay_rules_presets[_("Protect")] = {
109
      'add': 0,
109
      'add': 0,
110
      'append': 0,
110
      'append': 0,
111
      'remove': 0,
111
      'remove': 0,
112
      'delete': 0
112
      'delete': 0
113
    };
113
    };
114
    merge_rules_presets[_("Overwrite")] = {
114
    overlay_rules_presets[_("Overwrite")] = {
115
      'add': 1,
115
      'add': 1,
116
      'append': 1,
116
      'append': 1,
117
      'remove': 1,
117
      'remove': 1,
118
      'delete': 1
118
      'delete': 1
119
    };
119
    };
120
    merge_rules_presets[_("Add new")] = {
120
    overlay_rules_presets[_("Add new")] = {
121
      'add': 1,
121
      'add': 1,
122
      'append': 0,
122
      'append': 0,
123
      'remove': 0,
123
      'remove': 0,
124
      'delete': 0
124
      'delete': 0
125
    };
125
    };
126
    merge_rules_presets[_("Add and append")] = {
126
    overlay_rules_presets[_("Add and append")] = {
127
      'add': 1,
127
      'add': 1,
128
      'append': 1,
128
      'append': 1,
129
      'remove': 0,
129
      'remove': 0,
130
      'delete': 0
130
      'delete': 0
131
    };
131
    };
132
    merge_rules_presets[_("Protect from deletion")] = {
132
    overlay_rules_presets[_("Protect from deletion")] = {
133
      'add': 1,
133
      'add': 1,
134
      'append': 1,
134
      'append': 1,
135
      'remove': 1,
135
      'remove': 1,
136
      'delete': 0
136
      'delete': 0
137
    };
137
    };
138
138
139
    var merge_rules_label_to_value = {};
139
    var overlay_rules_label_to_value = {};
140
    merge_rules_label_to_value[_("Add")] = 1;
140
    overlay_rules_label_to_value[_("Add")] = 1;
141
    merge_rules_label_to_value[_("Append")] = 1;
141
    overlay_rules_label_to_value[_("Append")] = 1;
142
    merge_rules_label_to_value[_("Remove")] = 1;
142
    overlay_rules_label_to_value[_("Remove")] = 1;
143
    merge_rules_label_to_value[_("Delete")] = 1;
143
    overlay_rules_label_to_value[_("Delete")] = 1;
144
    merge_rules_label_to_value[_("Skip")] = 0;
144
    overlay_rules_label_to_value[_("Skip")] = 0;
145
145
146
    function hash_config(config) {
146
    function hash_config(config) {
147
      return JSON.stringify(config, Object.keys(config).sort());
147
      return JSON.stringify(config, Object.keys(config).sort());
148
    }
148
    }
149
149
150
    var merge_rules_preset_map = {};
150
    var overlay_rules_preset_map = {};
151
    $.each(merge_rules_presets, function(preset, config) {
151
    $.each(overlay_rules_presets, function(preset, config) {
152
      merge_rules_preset_map[hash_config(config)] = preset;
152
      overlay_rules_preset_map[hash_config(config)] = preset;
153
    });
153
    });
154
154
155
    function operations_config_merge_rule_preset(config) {
155
    function operations_config_overlay_rule_preset(config) {
156
      return merge_rules_preset_map[hash_config(config)] || '';
156
      return overlay_rules_preset_map[hash_config(config)] || '';
157
    }
157
    }
158
158
159
    /* Set preset values according to operation config */
159
    /* Set preset values according to operation config */
Lines 162-171 $(document).ready(function(){ Link Here
162
      var operations_config = {};
162
      var operations_config = {};
163
      $('.rule-operation-action', $this).each(function() {
163
      $('.rule-operation-action', $this).each(function() {
164
        var $operation = $(this);
164
        var $operation = $(this);
165
        operations_config[$operation.data('operation')] = merge_rules_label_to_value[$operation.text()];
165
        operations_config[$operation.data('operation')] = overlay_rules_label_to_value[$operation.text()];
166
      });
166
      });
167
      $('.rule-preset', $this).text(
167
      $('.rule-preset', $this).text(
168
        operations_config_merge_rule_preset(operations_config) || _("Custom")
168
        operations_config_overlay_rule_preset(operations_config) || _("Custom")
169
      );
169
      );
170
    });
170
    });
171
171
Lines 178-184 $(document).ready(function(){ Link Here
178
        operations_config[$this.attr('name')] = parseInt($this.val());
178
        operations_config[$this.attr('name')] = parseInt($this.val());
179
      });
179
      });
180
      $('select[name="preset"]', $parent_row).val(
180
      $('select[name="preset"]', $parent_row).val(
181
          operations_config_merge_rule_preset(operations_config)
181
          operations_config_overlay_rule_preset(operations_config)
182
      );
182
      );
183
    });
183
    });
184
184
Lines 188-194 $(document).ready(function(){ Link Here
188
      var $parent_row = $this.closest('tr');
188
      var $parent_row = $this.closest('tr');
189
      var preset = $this.val();
189
      var preset = $this.val();
190
      if (preset) {
190
      if (preset) {
191
        $.each(merge_rules_presets[preset], function(operation, action) {
191
        $.each(overlay_rules_presets[preset], function(operation, action) {
192
          $('select[name="' + operation + '"]', $parent_row).val(action);
192
          $('select[name="' + operation + '"]', $parent_row).val(action);
193
        });
193
        });
194
      }
194
      }
Lines 264-282 $(document).ready(function(){ Link Here
264
});
264
});
265
</script>
265
</script>
266
</head>
266
</head>
267
<body id="admin_marc-merge-rules" class="admin">
267
<body id="admin_marc-overlay-rules" class="admin">
268
[% INCLUDE 'header.inc' %]
268
[% INCLUDE 'header.inc' %]
269
[% INCLUDE 'cat-search.inc' %]
269
[% INCLUDE 'cat-search.inc' %]
270
270
271
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
271
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
272
 &rsaquo; MARC merge rules
272
 &rsaquo; MARC overlay rules
273
</div>
273
</div>
274
274
275
<div class="main container-fluid">
275
<div class="main container-fluid">
276
<div class="row">
276
<div class="row">
277
<div class="col-sm-10 col-sm-push-2">
277
<div class="col-sm-10 col-sm-push-2">
278
278
279
<h1>Manage MARC merge rules</h1>
279
<h1>Manage MARC overlay rules</h1>
280
280
281
[% FOR m IN messages %]
281
[% FOR m IN messages %]
282
  <div class="dialog [% m.type | html %]">
282
  <div class="dialog [% m.type | html %]">
Lines 291-299 $(document).ready(function(){ Link Here
291
  </div>
291
  </div>
292
[% END %]
292
[% END %]
293
293
294
[% UNLESS Koha.Preference( 'MARCMergeRules' ) %]
294
[% UNLESS Koha.Preference( 'MARCOverlayRules' ) %]
295
    <div class="dialog message">
295
    <div class="dialog message">
296
        The <b>MARCMergeRules</b> preference is not set, don't forget to enable it for rules to take effect.
296
        The <b>MARCOverlayRules</b> preference is not set, don't forget to enable it for rules to take effect.
297
    </div>
297
    </div>
298
[% END %]
298
[% END %]
299
[% IF removeConfirm %]
299
[% IF removeConfirm %]
Lines 308-315 $(document).ready(function(){ Link Here
308
</div>
308
</div>
309
[% END %]
309
[% END %]
310
310
311
<form action="[% script_name %]" method="POST" id="marc-merge-rules-form">
311
<form action="[% script_name %]" method="POST" id="marc-overlay-rules-form">
312
<table id="marc-merge-rules">
312
<table id="marc-overlay-rules">
313
    <thead><tr>
313
    <thead><tr>
314
        <th>Rule</th>
314
        <th>Rule</th>
315
        <th>Module</th>
315
        <th>Module</th>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref (-8 / +6 lines)
Lines 279-285 Cataloging: Link Here
279
                  1: "do"
279
                  1: "do"
280
                  0: "don't"
280
                  0: "don't"
281
            - attempt to match aggressively by trying all variations of the ISSNs in the imported record as a phrase in the ISSN fields of already cataloged records.
281
            - attempt to match aggressively by trying all variations of the ISSNs in the imported record as a phrase in the ISSN fields of already cataloged records.
282
282
        -
283
            - pref: MARCOverlayRules
284
              choices:
285
                  yes: "Use"
286
                  no: "Don't use"
287
            - MARC overlay rules for incoming records, to decide which action to take for each field.
283
    Exporting:
288
    Exporting:
284
        -
289
        -
285
            - "Include the following fields when exporting BibTeX:"
290
            - "Include the following fields when exporting BibTeX:"
Lines 305-314 Cataloging: Link Here
305
            - "All values of repeating tags and subfields will be printed with the given RIS tag."
310
            - "All values of repeating tags and subfields will be printed with the given RIS tag."
306
            - "<br/>"
311
            - "<br/>"
307
            - "Use of TY ( record type ) as a key will <em>replace</em> the default TY with the field value of your choosing."
312
            - "Use of TY ( record type ) as a key will <em>replace</em> the default TY with the field value of your choosing."
308
        -
309
            - When importing records
310
            - pref: MARCMergeRules
311
              choices:
312
                  yes: "use"
313
                  no: "don't use"
314
            - MARC merge rules to decide which action to take for each field.
(-)a/t/db_dependent/Biblio/MarcMergeRules.t (-33 / +30 lines)
Lines 22-40 use MARC::Record; Link Here
22
22
23
use C4::Context;
23
use C4::Context;
24
use C4::Biblio;
24
use C4::Biblio;
25
use Koha::Database; #??
25
use Koha::Database;
26
26
27
use Test::More tests => 23;
27
use Test::More tests => 23;
28
use Test::MockModule;
28
use Test::MockModule;
29
29
30
use Koha::MarcMergeRules;
30
use Koha::MarcOverlayRules;
31
31
32
use t::lib::Mocks;
32
use t::lib::Mocks;
33
33
34
my $schema = Koha::Database->schema;
34
my $schema = Koha::Database->schema;
35
$schema->storage->txn_begin;
35
$schema->storage->txn_begin;
36
36
37
t::lib::Mocks::mock_preference('MARCMergeRules', '1');
37
t::lib::Mocks::mock_preference('MARCOverlayRules', '1');
38
38
39
# Create a record
39
# Create a record
40
my $orig_record = MARC::Record->new();
40
my $orig_record = MARC::Record->new();
Lines 54-64 $incoming_record->append_fields( Link Here
54
    MARC::Field->new('501', '', '', 'a' => 'Two cold bottles of beer in the fridge'), # Added
54
    MARC::Field->new('501', '', '', 'a' => 'Two cold bottles of beer in the fridge'), # Added
55
);
55
);
56
56
57
# Test default behavior when MARCMergeRules is enabled, but no rules defined (overwrite)
57
# Test default behavior when MARCOverlayRules is enabled, but no rules defined (overwrite)
58
subtest 'Record fields has been overwritten when no merge rules are defined' => sub {
58
subtest 'Record fields has been overwritten when no merge rules are defined' => sub {
59
    plan tests => 4;
59
    plan tests => 4;
60
60
61
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
61
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
62
62
63
    my @all_fields = $merged_record->fields();
63
    my @all_fields = $merged_record->fields();
64
64
Lines 79-85 subtest 'Record fields has been overwritten when no merge rules are defined' => Link Here
79
    );
79
    );
80
};
80
};
81
81
82
my $rule =  Koha::MarcMergeRules->find_or_create({
82
my $rule =  Koha::MarcOverlayRules->find_or_create({
83
    tag => '*',
83
    tag => '*',
84
    module => 'source',
84
    module => 'source',
85
    filter => '*',
85
    filter => '*',
Lines 92-98 my $rule = Koha::MarcMergeRules->find_or_create({ Link Here
92
subtest 'Record fields has been protected when matched merge all rule operations are set to "0"' => sub {
92
subtest 'Record fields has been protected when matched merge all rule operations are set to "0"' => sub {
93
    plan tests => 3;
93
    plan tests => 3;
94
94
95
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
95
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
96
96
97
    my @all_fields = $merged_record->fields();
97
    my @all_fields = $merged_record->fields();
98
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
98
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
Lines 122-128 subtest 'Only new fields has been added when add = 1, append = 0, remove = 0, de Link Here
122
    );
122
    );
123
    $rule->store();
123
    $rule->store();
124
124
125
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
125
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
126
126
127
    my @all_fields = $merged_record->fields();
127
    my @all_fields = $merged_record->fields();
128
    cmp_ok(scalar @all_fields, '==', 5, "Record has the expected number of fields");
128
    cmp_ok(scalar @all_fields, '==', 5, "Record has the expected number of fields");
Lines 159-165 subtest 'Only appended fields has been added when add = 0, append = 1, remove = Link Here
159
    );
159
    );
160
    $rule->store();
160
    $rule->store();
161
161
162
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
162
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
163
163
164
    my @all_fields = $merged_record->fields();
164
    my @all_fields = $merged_record->fields();
165
    cmp_ok(scalar @all_fields, '==', 4, "Record has the expected number of fields");
165
    cmp_ok(scalar @all_fields, '==', 4, "Record has the expected number of fields");
Lines 191-197 subtest 'Appended and added fields has been added when add = 1, append = 1, remo Link Here
191
    );
191
    );
192
    $rule->store();
192
    $rule->store();
193
193
194
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
194
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
195
195
196
    my @all_fields = $merged_record->fields();
196
    my @all_fields = $merged_record->fields();
197
    cmp_ok(scalar @all_fields, '==', 6, "Record has the expected number of fields");
197
    cmp_ok(scalar @all_fields, '==', 6, "Record has the expected number of fields");
Lines 228-234 subtest 'Record fields has been only removed when add = 0, append = 0, remove = Link Here
228
    );
228
    );
229
    $rule->store();
229
    $rule->store();
230
230
231
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
231
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
232
232
233
    my @all_fields = $merged_record->fields();
233
    my @all_fields = $merged_record->fields();
234
    cmp_ok(scalar @all_fields, '==', 2, "Record has the expected number of fields");
234
    cmp_ok(scalar @all_fields, '==', 2, "Record has the expected number of fields");
Lines 258-264 subtest 'Record fields has been added and removed when add = 1, append = 0, remo Link Here
258
    );
258
    );
259
    $rule->store();
259
    $rule->store();
260
260
261
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
261
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
262
262
263
    my @all_fields = $merged_record->fields();
263
    my @all_fields = $merged_record->fields();
264
    cmp_ok(scalar @all_fields, '==', 4, "Record has the expected number of fields");
264
    cmp_ok(scalar @all_fields, '==', 4, "Record has the expected number of fields");
Lines 294-300 subtest 'Record fields has been appended and removed when add = 0, append = 1, r Link Here
294
    );
294
    );
295
    $rule->store();
295
    $rule->store();
296
296
297
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
297
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
298
298
299
    my @all_fields = $merged_record->fields();
299
    my @all_fields = $merged_record->fields();
300
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
300
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
Lines 324-330 subtest 'Record fields has been added, appended and removed when add = 0, append Link Here
324
    );
324
    );
325
    $rule->store();
325
    $rule->store();
326
326
327
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
327
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
328
328
329
    my @all_fields = $merged_record->fields();
329
    my @all_fields = $merged_record->fields();
330
    cmp_ok(scalar @all_fields, '==', 5, "Record has the expected number of fields");
330
    cmp_ok(scalar @all_fields, '==', 5, "Record has the expected number of fields");
Lines 361-367 subtest 'Record fields has been deleted when add = 0, append = 0, remove = 0, de Link Here
361
    );
361
    );
362
    $rule->store();
362
    $rule->store();
363
363
364
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
364
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
365
365
366
    my @all_fields = $merged_record->fields();
366
    my @all_fields = $merged_record->fields();
367
    cmp_ok(scalar @all_fields, '==', 2, "Record has the expected number of fields");
367
    cmp_ok(scalar @all_fields, '==', 2, "Record has the expected number of fields");
Lines 386-392 subtest 'Record fields has been added and deleted when add = 1, append = 0, remo Link Here
386
    );
386
    );
387
    $rule->store();
387
    $rule->store();
388
388
389
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
389
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
390
390
391
    my @all_fields = $merged_record->fields();
391
    my @all_fields = $merged_record->fields();
392
    cmp_ok(scalar @all_fields, '==', 4, "Record has the expected number of fields");
392
    cmp_ok(scalar @all_fields, '==', 4, "Record has the expected number of fields");
Lines 417-423 subtest 'Record fields has been appended and deleted when add = 0, append = 1, r Link Here
417
    );
417
    );
418
    $rule->store();
418
    $rule->store();
419
419
420
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
420
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
421
421
422
    my @all_fields = $merged_record->fields();
422
    my @all_fields = $merged_record->fields();
423
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
423
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
Lines 442-448 subtest 'Record fields has been added, appended and deleted when add = 1, append Link Here
442
    );
442
    );
443
    $rule->store();
443
    $rule->store();
444
444
445
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
445
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
446
446
447
    my @all_fields = $merged_record->fields();
447
    my @all_fields = $merged_record->fields();
448
    cmp_ok(scalar @all_fields, '==', 5, "Record has the expected number of fields");
448
    cmp_ok(scalar @all_fields, '==', 5, "Record has the expected number of fields");
Lines 473-479 subtest 'Record fields has been removed and deleted when add = 0, append = 0, re Link Here
473
    );
473
    );
474
    $rule->store();
474
    $rule->store();
475
475
476
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
476
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
477
477
478
    my @all_fields = $merged_record->fields();
478
    my @all_fields = $merged_record->fields();
479
    cmp_ok(scalar @all_fields, '==', 1, "Record has the expected number of fields");
479
    cmp_ok(scalar @all_fields, '==', 1, "Record has the expected number of fields");
Lines 498-504 subtest 'Record fields has been added, removed and deleted when add = 1, append Link Here
498
    );
498
    );
499
    $rule->store();
499
    $rule->store();
500
500
501
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
501
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
502
502
503
    my @all_fields = $merged_record->fields();
503
    my @all_fields = $merged_record->fields();
504
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
504
    cmp_ok(scalar @all_fields, '==', 3, "Record has the expected number of fields");
Lines 529-535 subtest 'Record fields has been appended, removed and deleted when add = 0, appe Link Here
529
    );
529
    );
530
    $rule->store();
530
    $rule->store();
531
531
532
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
532
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
533
533
534
    my @all_fields = $merged_record->fields();
534
    my @all_fields = $merged_record->fields();
535
    cmp_ok(scalar @all_fields, '==', 2, "Record has the expected number of fields");
535
    cmp_ok(scalar @all_fields, '==', 2, "Record has the expected number of fields");
Lines 554-560 subtest 'Record fields has been overwritten when add = 1, append = 1, remove = 1 Link Here
554
    );
554
    );
555
    $rule->store();
555
    $rule->store();
556
556
557
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
557
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
558
558
559
    my @all_fields = $merged_record->fields();
559
    my @all_fields = $merged_record->fields();
560
560
Lines 578-584 subtest 'Record fields has been overwritten when add = 1, append = 1, remove = 1 Link Here
578
# Test rule tag specificity
578
# Test rule tag specificity
579
579
580
# Protect field 500 with more specific tag value
580
# Protect field 500 with more specific tag value
581
my $skip_all_rule = Koha::MarcMergeRules->find_or_create({
581
my $skip_all_rule = Koha::MarcOverlayRules->find_or_create({
582
    tag => '500',
582
    tag => '500',
583
    module => 'source',
583
    module => 'source',
584
    filter => '*',
584
    filter => '*',
Lines 591-597 my $skip_all_rule = Koha::MarcMergeRules->find_or_create({ Link Here
591
subtest '"500" field has been protected when rule matching on tag "500" is add = 0, append = 0, remove = 0, delete = 0' => sub {
591
subtest '"500" field has been protected when rule matching on tag "500" is add = 0, append = 0, remove = 0, delete = 0' => sub {
592
    plan tests => 4;
592
    plan tests => 4;
593
593
594
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
594
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
595
595
596
    my @all_fields = $merged_record->fields();
596
    my @all_fields = $merged_record->fields();
597
597
Lines 626-632 subtest '"5XX" fields has been protected when rule matching on regexp "5\d{2}" i Link Here
626
    );
626
    );
627
    $skip_all_rule->store();
627
    $skip_all_rule->store();
628
628
629
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
629
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
630
630
631
    my @all_fields = $merged_record->fields();
631
    my @all_fields = $merged_record->fields();
632
632
Lines 655-661 subtest 'Record fields has been overwritten when non wild card rule with filter Link Here
655
    );
655
    );
656
    $rule->store();
656
    $rule->store();
657
657
658
    my $merged_record = Koha::MarcMergeRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
658
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
659
659
660
    my @all_fields = $merged_record->fields();
660
    my @all_fields = $merged_record->fields();
661
661
Lines 679-685 subtest 'Record fields has been overwritten when non wild card rule with filter Link Here
679
subtest 'An exception is thrown when append = 1, remove = 0 is set for control field rule' => sub {
679
subtest 'An exception is thrown when append = 1, remove = 0 is set for control field rule' => sub {
680
    plan tests => 2;
680
    plan tests => 2;
681
    my $exception = try {
681
    my $exception = try {
682
        Koha::MarcMergeRules->validate({
682
        Koha::MarcOverlayRules->validate({
683
            'tag' => '008',
683
            'tag' => '008',
684
            'append' => 1,
684
            'append' => 1,
685
            'remove' => 0,
685
            'remove' => 0,
Lines 689-702 subtest 'An exception is thrown when append = 1, remove = 0 is set for control f Link Here
689
        return $_;
689
        return $_;
690
    };
690
    };
691
    ok(defined $exception, "Exception was caught");
691
    ok(defined $exception, "Exception was caught");
692
    ok($exception->isa('Koha::Exceptions::MarcMergeRule::InvalidControlFieldActions'), "Exception is of correct class");
692
    ok($exception->isa('Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions'), "Exception is of correct class");
693
};
693
};
694
694
695
subtest 'An exception is thrown when rule tag is set to invalid regexp' => sub {
695
subtest 'An exception is thrown when rule tag is set to invalid regexp' => sub {
696
    plan tests => 2;
696
    plan tests => 2;
697
697
698
    my $exception = try {
698
    my $exception = try {
699
        Koha::MarcMergeRules->validate({
699
        Koha::MarcOverlayRules->validate({
700
            'tag' => '**'
700
            'tag' => '**'
701
        });
701
        });
702
    }
702
    }
Lines 704-710 subtest 'An exception is thrown when rule tag is set to invalid regexp' => sub { Link Here
704
        return $_;
704
        return $_;
705
    };
705
    };
706
    ok(defined $exception, "Exception was caught");
706
    ok(defined $exception, "Exception was caught");
707
    ok($exception->isa('Koha::Exceptions::MarcMergeRule::InvalidTagRegExp'), "Exception is of correct class");
707
    ok($exception->isa('Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp'), "Exception is of correct class");
708
};
708
};
709
709
710
$skip_all_rule->delete();
710
$skip_all_rule->delete();
Lines 775-779 subtest 'context option in ModBiblio is handled correctly' => sub { Link Here
775
$rule->delete();
775
$rule->delete();
776
776
777
$schema->storage->txn_rollback;
777
$schema->storage->txn_rollback;
778
779
1;
780
- 

Return to bug 14957