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

(-)a/Koha/MarcOverlayRules.pm (-5 / +75 lines)
Lines 16-28 package Koha::MarcOverlayRules; Link Here
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
use List::Util qw(first);
19
use List::Util qw(first any);
20
use Koha::MarcOverlayRule;
20
use Koha::MarcOverlayRule;
21
use Carp;
21
use Carp;
22
22
23
use Koha::Exceptions::MarcOverlayRule;
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
use Clone qw(clone);
26
27
27
use parent qw(Koha::Objects);
28
use parent qw(Koha::Objects);
28
29
Lines 46-51 sub operations { Link Here
46
    return ('add', 'append', 'remove', 'delete');
47
    return ('add', 'append', 'remove', 'delete');
47
}
48
}
48
49
50
=head3 modules
51
52
Returns a list of all modules in order of priority.
53
54
=cut
55
56
sub modules {
57
    return ('userid', 'categorycode', 'source');
58
}
59
60
49
=head3 context_rules
61
=head3 context_rules
50
62
51
    my $rules = Koha::MarcOverlayRules->context_rules($context);
63
    my $rules = Koha::MarcOverlayRules->context_rules($context);
Lines 96-114 sub context_rules { Link Here
96
        $cache->set_in_cache('marc_overlay_rules', $rules);
108
        $cache->set_in_cache('marc_overlay_rules', $rules);
97
    }
109
    }
98
110
99
    my $context_rules = undef;
111
    my $context_rules;
100
    foreach my $module_name (keys %{$context}) {
112
    my @context_modules = grep { exists $context->{$_} } $self->modules();
113
114
    foreach my $module_name (@context_modules) {
101
        if (
115
        if (
102
            exists $rules->{$module_name} &&
116
            exists $rules->{$module_name} &&
103
            exists $rules->{$module_name}->{$context->{$module_name}}
117
            exists $rules->{$module_name}->{$context->{$module_name}}
104
        ) {
118
        ) {
105
            $context_rules = $rules->{$module_name}->{$context->{$module_name}};
119
            $context_rules = $rules->{$module_name}->{$context->{$module_name}};
120
121
            unless (exists $rules->{$module_name}->{'*'}) {
122
                # No wildcard filter rules defined
123
                last;
124
            }
125
126
            # Merge in all wildcard filter rules which has not been overriden
127
            # by the matching context
128
            if ($context_rules->{'*'}) {
129
                # Wildcard tag for matching context will override all rules,
130
                # nothing left to do
131
                last;
132
            }
133
134
            # Clone since we will potentially be modified cached value
135
            # fetched with unsafe => 1
136
            $context_rules = clone($context_rules);
137
138
            if (exists $rules->{$module_name}->{'*'}->{'*'}) {
139
                # Merge in wildcard filter wildcard tag rule if exists
140
                $context_rules->{'*'} = $rules->{$module_name}->{'*'}->{'*'};
141
            }
142
143
            if (exists $rules->{$module_name}->{'*'}->{tags}) {
144
                if (exists $context_rules->{regexps}) {
145
                    # If the current context has regexp rules, we have to make sure
146
                    # to not only to skip tags already present but also those matching
147
                    # any of those rules
148
149
                    my @regexps = map { qr/^$_->[0]$/ } @{$context_rules->{regexps}};
150
151
                    foreach my $tag (keys %{$rules->{$module_name}->{'*'}->{tags}}) {
152
                        unless ((any { $tag =~ $_ } @regexps) || exists $context_rules->{tags}->{$tag}) {
153
                            $context_rules->{tags}->{$tag} = $rules->{$module_name}->{'*'}->{tags}->{$tag};
154
                        }
155
                    }
156
                }
157
                else {
158
                    # Merge in wildcard filter tag rules not already present
159
                    # in the matching context
160
                    foreach my $tag (keys %{$rules->{$module_name}->{'*'}->{tags}}) {
161
                        unless (exists $context_rules->{tags}->{$tag}) {
162
                            $context_rules->{tags}->{$tag} = $rules->{$module_name}->{'*'}->{tags}->{$tag};
163
                        }
164
                    }
165
                }
166
            }
167
168
            if (exists $rules->{$module_name}->{'*'}->{regexps}) {
169
                # Merge in wildcard filter regexp rules last, making sure rules from the
170
                # matching context have precidence
171
                $context_rules->{regexps} //= [];
172
                push @{$context_rules->{regexps}}, @{$rules->{$module_name}->{'*'}->{regexps}};
173
            }
174
106
            last;
175
            last;
107
        }
176
        }
108
    }
177
    }
178
109
    if (!$context_rules) {
179
    if (!$context_rules) {
110
        # No perms matching specific context conditions found, try wildcard value for each active context
180
        # No rules matching specific context conditions found, try wildcard value for each active context
111
        foreach my $module_name (keys %{$context}) {
181
        foreach my $module_name (@context_modules) {
112
            if (exists $rules->{$module_name}->{'*'}) {
182
            if (exists $rules->{$module_name}->{'*'}) {
113
                $context_rules = $rules->{$module_name}->{'*'};
183
                $context_rules = $rules->{$module_name}->{'*'};
114
                last;
184
                last;
(-)a/t/db_dependent/Biblio/MarcOverlayRules.t (-14 / +182 lines)
Lines 64-82 my $orig_record = build_record([ Link Here
64
    [ '250', 'a', '250 bottles of beer on the wall' ],
64
    [ '250', 'a', '250 bottles of beer on the wall' ],
65
    [ '250', 'a', '256 bottles of beer on the wall' ],
65
    [ '250', 'a', '256 bottles of beer on the wall' ],
66
    [ '500', 'a', 'One bottle of beer in the fridge' ],
66
    [ '500', 'a', 'One bottle of beer in the fridge' ],
67
]
67
]);
68
);
69
68
70
my $incoming_record = build_record(
69
my $incoming_record = build_record([
71
    [
72
    ['250', 'a', '256 bottles of beer on the wall'], # Unchanged
70
    ['250', 'a', '256 bottles of beer on the wall'], # Unchanged
73
    ['250', 'a', '251 bottles of beer on the wall'], # Appended
71
    ['250', 'a', '251 bottles of beer on the wall'], # Appended
74
    #['250', 'a', '250 bottles of beer on the wall'], # Removed
72
    #['250', 'a', '250 bottles of beer on the wall'], # Removed
75
    #['500', 'a', 'One bottle of beer in the fridge'], # Deleted
73
    #['500', 'a', 'One bottle of beer in the fridge'], # Deleted
76
    ['501', 'a', 'One cold bottle of beer in the fridge'], # Added
74
    ['501', 'a', 'One cold bottle of beer in the fridge'], # Added
77
    ['501', 'a', 'Two cold bottles of beer in the fridge'], # Added
75
    ['501', 'a', 'Two cold bottles of beer in the fridge'], # Added
78
]
76
]);
79
);
80
77
81
# Test default behavior when MARCOverlayRules is enabled, but no rules defined (overwrite)
78
# Test default behavior when MARCOverlayRules is enabled, but no rules defined (overwrite)
82
subtest 'No rule defined' => sub {
79
subtest 'No rule defined' => sub {
Lines 685-708 subtest '"5XX" fields has been protected when rule matching on regexp "5\d{2}" i Link Here
685
682
686
$skip_all_rule->delete();
683
$skip_all_rule->delete();
687
684
688
# Test module specificity, the 0 all rule should no longer be included in set of applied rules
685
# Test module filter specificity
689
subtest 'Record fields has been overwritten when non wild card rule with filter match is add = 1, append = 1, remove = 1, delete = 1' => sub {
686
subtest 'Module filter precedence tests' => sub {
690
    plan tests => 1;
687
    plan tests => 7;
691
688
692
    $rule->set(
689
    $rule->set(
693
        {
690
        {
694
            'filter' => 'test',
691
            tag => '*',
692
            module => 'source',
693
            filter => '*',
694
            add => 0,
695
            append => 0,
696
            remove => 0,
697
            delete => 0,
695
        }
698
        }
696
    )->store();
699
    )->store();
697
700
701
    my $matching_filter_rule_overwrite = Koha::MarcOverlayRules->find_or_create(
702
        {
703
            tag => '*',
704
            module => 'source',
705
            filter => 'test',
706
            add => 1,
707
            append => 1,
708
            remove => 1,
709
            delete => 1
710
        }
711
    );
712
713
    # Current rules:
714
    # source: *, tag: *, add: 0, append: 0, remove: 0, delete: 0
715
    # source: test, tag: *, add: 1, append: 1, remove: 1, delete: 1
698
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
716
    my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
699
717
700
    is(
718
    is(
701
        $merged_record->as_formatted,
719
        $merged_record->as_formatted,
702
        $incoming_record->as_formatted,
720
        $incoming_record->as_formatted,
703
        'Only existing rule is not for us, erasing' # FIXME Is this comment correct?
721
        'Non wildcard rule with wildcard tag matches and overrides wildcard rule with wildcard tag'
722
    );
723
724
    $rule->set(
725
        {
726
            tag => '5\d{2}'
727
        }
728
    )->store();
729
730
    # Current rules:
731
    # source: *, tag: 5\d{2}, add: 0, append: 0, remove: 0, delete: 0
732
    # source: test, tag: *, add: 1, append: 1, remove: 1, delete: 1
733
    $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
734
735
    is(
736
        $merged_record->as_formatted,
737
        $incoming_record->as_formatted,
738
        'Non wildcard rule with wildcard tag matches and overrides wildcard rules with regexp tags'
739
    );
740
741
    $rule->set(
742
        {
743
            tag => '501'
744
        }
745
    )->store();
746
747
    # Current rules:
748
    # source: *, tag: 501, add: 0, append: 0, remove: 0, delete: 0
749
    # source: test, tag: *, add: 1, append: 1, remove: 1, delete: 1
750
    $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
751
752
    is(
753
        $merged_record->as_formatted,
754
        $incoming_record->as_formatted,
755
        'Non wildcard rule with wildcard tag matches and overrides wildcard rules with specific tags'
704
    );
756
    );
705
757
758
    $rule->set(
759
        {
760
            tag => '*'
761
        }
762
    )->store();
763
764
    my $wildcard_filter_rule_overwrite = Koha::MarcOverlayRules->find_or_create(
765
        {
766
            tag => '501',
767
            module => 'source',
768
            filter => '*',
769
            add => 1,
770
            append => 1,
771
            remove => 1,
772
            delete => 1,
773
        }
774
    );
775
776
    $matching_filter_rule_overwrite->set(
777
        {
778
            tag => '250'
779
        }
780
    )->store();
781
782
    # Current rules:
783
    # source: *, tag: *, add: 0, append: 0, remove: 0, delete: 0
784
    # source: *, tag: 501, add: 1, append: 1, remove: 1, delete: 1
785
    # source: test, tag: 250, add: 1, append: 1, remove: 1, delete: 1
786
    $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
787
788
    my $expected_record = build_record([
789
        ['250', 'a', '256 bottles of beer on the wall'], # Unchanged
790
        ['250', 'a', '251 bottles of beer on the wall'], # Appended
791
        #['250', 'a', '250 bottles of beer on the wall'], # Removed
792
        ['500', 'a', 'One bottle of beer in the fridge'], # Protected
793
        ['501', 'a', 'One cold bottle of beer in the fridge'], # Added
794
        ['501', 'a', 'Two cold bottles of beer in the fridge'], # Added
795
    ]);
796
797
    is(
798
        $merged_record->as_formatted,
799
        $expected_record->as_formatted,
800
        'Rules have been merged when non wildcard filter context does not contain any wildcard tag rules'
801
    );
802
803
    my $matching_filter_rule_protect = Koha::MarcOverlayRules->find_or_create(
804
        {
805
            tag => '501',
806
            module => 'source',
807
            filter => 'test',
808
            add => 0,
809
            append => 0,
810
            remove => 0,
811
            delete => 0
812
        }
813
    );
814
815
    # Current rules:
816
    # source: *, tag: *, add: 0, append: 0, remove: 0, delete: 0
817
    # source: *, tag: 501, add: 1, append: 1, remove: 1, delete: 1
818
    # source: test, tag: 250, add: 1, append: 1, remove: 1, delete: 1
819
    # source: test, tag: 501, add: 0, append: 0, remove: 0, delete: 0
820
    $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
821
822
    $expected_record = build_record([
823
        ['250', 'a', '256 bottles of beer on the wall'], # Unchanged
824
        ['250', 'a', '251 bottles of beer on the wall'], # Appended
825
        #['250', 'a', '250 bottles of beer on the wall'], # Removed
826
        ['500', 'a', 'One bottle of beer in the fridge'], # Protected
827
        #['501', 'a', 'One cold bottle of beer in the fridge'], # Protected
828
        #['501', 'a', 'Two cold bottles of beer in the fridge'], # Protected
829
    ]);
830
831
    is(
832
        $merged_record->as_formatted,
833
        $expected_record->as_formatted,
834
        'Wildcard filter rule has been overridden by non wildcard filter rule with same tag'
835
    );
836
837
    $matching_filter_rule_protect->set({
838
        tag => '5\d{2}',
839
    })->store();
840
841
    # Current rules:
842
    # source: *, tag: *, add: 0, append: 0, remove: 0, delete: 0
843
    # source: *, tag: 501, add: 1, append: 1, remove: 1, delete: 1
844
    # source: test, tag: 250, add: 1, append: 1, remove: 1, delete: 1
845
    # source: test, tag: 5\d{2}, add: 0, append: 0, remove: 0, delete: 0
846
    $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
847
848
    is(
849
        $merged_record->as_formatted,
850
        $expected_record->as_formatted,
851
        'Wildcard filter rules with tags that matches tag regexps for non wildcard filter rules has been overridden'
852
    );
853
854
    $wildcard_filter_rule_overwrite->set({
855
        tag => '5\d{2}',
856
    })->store();
857
858
    # Current rules:
859
    # source: *, tag: *, add: 0, append: 0, remove: 0, delete: 0
860
    # source: *, tag: 5\d{2}, add: 1, append: 1, remove: 1, delete: 1
861
    # source: test, tag: 250, add: 1, append: 1, remove: 1, delete: 1
862
    # source: test, tag: 5\d{2}, add: 0, append: 0, remove: 0, delete: 0
863
    $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
864
865
    is(
866
        $merged_record->as_formatted,
867
        $expected_record->as_formatted,
868
        'Wildcard filter rules with tags with tag regexps matching the same tag as regexps for non wildcard filter rules has been overridden'
869
    );
870
871
    $wildcard_filter_rule_overwrite->delete();
872
    $matching_filter_rule_overwrite->delete();
873
    $matching_filter_rule_protect->delete();
706
};
874
};
707
875
708
subtest 'An exception is thrown when append = 1, remove = 0 is set for control field rule' => sub {
876
subtest 'An exception is thrown when append = 1, remove = 0 is set for control field rule' => sub {
Lines 765-771 subtest 'context option in ModBiblio is handled correctly' => sub { Link Here
765
    my $expected_record = build_record([
933
    my $expected_record = build_record([
766
            # "250" field has been appended
934
            # "250" field has been appended
767
            [ '250', 'a', '250 bottles of beer on the wall' ],        # original
935
            [ '250', 'a', '250 bottles of beer on the wall' ],        # original
768
            [ '250', 'a', '256 bottles of beer on the wall' ],        # incoming
936
            [ '250', 'a', '256 bottles of beer on the wall' ],        # original
769
            [ '500', 'a', 'One bottle of beer in the fridge' ],       # original
937
            [ '500', 'a', 'One bottle of beer in the fridge' ],       # original
770
            [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ],    # created by AddBiblio
938
            [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ],    # created by AddBiblio
771
    ]);
939
    ]);
Lines 788-797 subtest 'context option in ModBiblio is handled correctly' => sub { Link Here
788
    my $updated_record = $biblio->get_from_storage->metadata->record;
956
    my $updated_record = $biblio->get_from_storage->metadata->record;
789
957
790
    $expected_record = build_record([
958
    $expected_record = build_record([
791
            # "250" field has been appended
959
            # "250" field has been protected
792
            [ '250', 'a', '250 bottles of beer on the wall' ],
960
            [ '250', 'a', '250 bottles of beer on the wall' ],
793
            [ '250', 'a', '256 bottles of beer on the wall' ],
961
            [ '250', 'a', '256 bottles of beer on the wall' ],
794
            [ '500', 'a', 'One bottle of beer in the fridge' ],
962
            [ '500', 'a', 'One bottle of beer in the fridge' ],
963
            # "500" field has been appended
795
            [ '500', 'a', 'One cold bottle of beer in the fridge' ],
964
            [ '500', 'a', 'One cold bottle of beer in the fridge' ],
796
            [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ],    # created by AddBiblio
965
            [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ],    # created by AddBiblio
797
    ]);
966
    ]);
798
- 

Return to bug 33268