Bugzilla – Attachment 7323 Details for
Bug 7284
Authority matching algorithm improvements
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 7284: Authority matching improvements
Bug-7284-Authority-matching-improvements.patch (text/plain), 160.53 KB, created by
Jared Camins-Esakov
on 2012-01-24 17:09:04 UTC
(
hide
)
Description:
Bug 7284: Authority matching improvements
Filename:
MIME Type:
Creator:
Jared Camins-Esakov
Created:
2012-01-24 17:09:04 UTC
Size:
160.53 KB
patch
obsolete
>From 95ecc259dff0f766682e71b9c4e0b27d4c1eed70 Mon Sep 17 00:00:00 2001 >From: Jared Camins-Esakov <jcamins@cpbibliography.com> >Date: Sat, 31 Dec 2011 12:17:41 -0500 >Subject: [PATCH] Bug 7284: Authority matching improvements >Content-Type: text/plain; charset="UTF-8" > >Squashed patch incorporating all previous patches. > >1. Cleaned up authorities code by removing unused functions and adding >unimplemented functions and added some unit tests. > >2. Added an additional box to the authority finder plugin for "Heading match," >which consults not just the main entry but also See-from and See-also-from >headings. > >3. Improved Koha's authority linker cron job (misc/link_bibs_to_authorities.pl) >to make it more useful: > >Added the following options to the misc/link_bibs_to_authorities.pl script: >--auth-limit Only process those headings that match the authorities > matching the user-specified WHERE clause. >--bib-limit Only process those bib records that match the > user-specified WHERE clause. >--commit Commit the results to the database after every N records > are processed. >--link-report Display a report of all the headings that were processed. > >Converted misc/link_bibs_to_authorities.pl to use POD. > >Added a detailed report of headings that linked, did not link, and linked >in a "fuzzy" fashion (the exact semantics of fuzzy are up to the individual >linker modules) during the run. > >Implemented new C4::Linker functionality to make it possible to easily add >custom authority linker algorithms. Currently available linker options are: >* Default: retains the current behavior of only creating links when there is > an exact match to one and only one authority record; if the 'broader_headings' > option is enabled, it will try to link to headings to authority records for > broader headings by removing subfields from the end of the heading (NOTE: > test the results before enabling broader_headings in a production system > because its usefulness is very much dependent on individual sites' authority > files) >* First Match: based on Default, creates a link to the *first* authority > record that matches a given heading, even if there is more than one > authority record that matches >* Last Match: based on Default, creates a link to the *last* authority > record that matches a given heading, even if there is more than one record > that matches > >Made the linking functionality use the SearchAuthorities in C4::AuthoritiesMarc >rather than SimpleSearch in C4::Search. Once C4::Search has been refactored, >SearchAuthorities should be rewritten to simply call into C4::Search. However, >at this time C4::Search cannot handle authority searching. Also fixed numerous >performance issues in SearchAuthorities and the Linker script: >* Correctly destroy ZOOM recordsets in SearchAuthorities when finished. If left > undestroyed, efficiency appears to approach O(log n^n) >* Add an optional $skipmetadata flag to SearchAuthorities that can be used to > avoid additional calls into Zebra when all that is wanted are authority > records and not statistics about their use > >This patch also adds the following sysprefs: >* AutoCreateAuthorities - When this and BiblioAddsAuthorities are both turned > on, automatically create authority records for headings that don't have > any authority link when cataloging. When BiblioAddsAuthorities is on and > AutoCreateAuthorities is turned off, do not automatically generate authority > records, but allow the user to enter headings that don't match an existing > authority. When BiblioAddsAuthorities is off, this has no effect. >* LinkerModule - Chooses which linker module to use for matching headings > (current options are as described above in the section on linker options: > "Default," "FirstMatch," and "LastMatch") >* LinkerOptions - A pipe-separated list of options to set for the authority > linker >* LinkerRelink - When turned on, the linker will confirm the links for headings > that have previously been linked to an authority record when it runs. When > turned off, any heading with an existing link will be ignored. >* LinkerKeepStale - When turned on, the linker will never *delete* a link to an > authority record, though, depending on the value of LinkerRelink, it may > change the link. > >This patch also modifies the authority indexing to remove trailing punctuation >from Match indexes. > >4. Replace the old BiblioAddAuthorities subroutines with calls into the new >C4::Linker routines. > >5. Add a simple implementation for C4::Heading::UNIMARC. (With thanks to F. >Demians, 2011.01.09:) Correct C4::Heading::UNIMARC class loading. Create biblio >tag to authority types data structure at initialization rather than querying DB. > >6. Ran perltidy on all changed code. > >Signed-off-by: Jared Camins-Esakov <jcamins@cpbibliography.com> >--- > C4/AuthoritiesMarc.pm | 185 +++---- > C4/Biblio.pm | 132 +++++- > C4/Heading.pm | 155 ++++-- > C4/Heading/MARC21.pm | 198 +++++--- > C4/Heading/UNIMARC.pm | 207 ++++++++ > C4/Linker.pm | 123 +++++ > C4/Linker/Default.pm | 97 ++++ > C4/Linker/FirstMatch.pm | 58 +++ > C4/Linker/LastMatch.pm | 58 +++ > C4/Search.pm | 101 ---- > acqui/addorderiso2709.pl | 4 +- > acqui/neworderempty.pl | 4 +- > authorities/auth_finder.pl | 8 +- > cataloguing/addbiblio.pl | 91 +---- > etc/zebradb/authorities/etc/bib1.att | 1 + > .../authorities/authority-koha-indexdefs.xml | 64 +++ > .../authorities/authority-zebra-indexdefs.xsl | 537 ++++++++++++++++++-- > .../marc21/authorities/koha-indexdefs-to-zebra.xsl | 21 +- > .../marc_defs/unimarc/authorities/record.abs | 120 +++--- > .../atomicupdate/bug_7284_authority_linking_pt1 | 21 + > installer/data/mysql/sysprefs.sql | 5 + > .../prog/en/includes/auth-finder-search.inc | 14 +- > .../en/modules/admin/preferences/authorities.pref | 94 +++- > misc/link_bibs_to_authorities.pl | 267 ++++++++-- > t/Heading_MARC21.t | 8 +- > t/db_dependent/Linker_FirstMatch.t | 46 ++ > t/db_dependent/lib/KohaTest/AuthoritiesMarc.pm | 1 - > 27 files changed, 1987 insertions(+), 633 deletions(-) > create mode 100644 C4/Heading/UNIMARC.pm > create mode 100644 C4/Linker.pm > create mode 100644 C4/Linker/Default.pm > create mode 100644 C4/Linker/FirstMatch.pm > create mode 100644 C4/Linker/LastMatch.pm > create mode 100644 installer/data/mysql/atomicupdate/bug_7284_authority_linking_pt1 > create mode 100755 t/db_dependent/Linker_FirstMatch.t > >diff --git a/C4/AuthoritiesMarc.pm b/C4/AuthoritiesMarc.pm >index b3c5070..c3b0db3 100644 >--- a/C4/AuthoritiesMarc.pm >+++ b/C4/AuthoritiesMarc.pm >@@ -17,9 +17,8 @@ package C4::AuthoritiesMarc; > # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > > use strict; >-#use warnings; FIXME - Bug 2505 >+use warnings; > use C4::Context; >-use C4::Koha; > use MARC::Record; > use C4::Biblio; > use C4::Search; >@@ -41,7 +40,6 @@ BEGIN { > &GetAuthType > &GetAuthTypeCode > &GetAuthMARCFromKohaField >- &AUTHhtml2marc > > &AddAuthority > &ModAuthority >@@ -98,15 +96,16 @@ sub GetAuthMARCFromKohaField { > =head2 SearchAuthorities > > (\@finalresult, $nbresults)= &SearchAuthorities($tags, $and_or, >- $excluding, $operator, $value, $offset,$length,$authtypecode,$sortby) >+ $excluding, $operator, $value, $offset,$length,$authtypecode, >+ $sortby[, $skipmetadata]) > > returns ref to array result and count of results returned > > =cut > > sub SearchAuthorities { >- my ($tags, $and_or, $excluding, $operator, $value, $offset,$length,$authtypecode,$sortby) = @_; >-# warn "CALL : $tags, $and_or, $excluding, $operator, $value, $offset,$length,$authtypecode,$sortby"; >+ my ($tags, $and_or, $excluding, $operator, $value, $offset,$length,$authtypecode,$sortby,$skipmetadata) = @_; >+ # warn Dumper($tags, $and_or, $excluding, $operator, $value, $offset,$length,$authtypecode,$sortby); > my $dbh=C4::Context->dbh; > if (C4::Context->preference('NoZebra')) { > >@@ -225,23 +224,45 @@ sub SearchAuthorities { > for(my $i = 0 ; $i <= $#{$value} ; $i++) > { > if (@$value[$i]){ >- if (@$tags[$i] eq "mainmainentry") { >- >- $attr =" \@attr 1=Heading-Main "; >- >- }elsif (@$tags[$i] eq "mainentry") { >- $attr =" \@attr 1=Heading "; >- }else{ >- $attr =" \@attr 1=Any "; >+ if ( @$tags[$i] eq "mainmainentry" ) { >+ $attr = " \@attr 1=Heading-Main "; > } >- if (@$operator[$i] eq 'is') { >- $attr.=" \@attr 4=1 \@attr 5=100 ";##Phrase, No truncation,all of subfield field must match >- }elsif (@$operator[$i] eq "="){ >- $attr.=" \@attr 4=107 "; #Number Exact match >- }elsif (@$operator[$i] eq "start"){ >- $attr.=" \@attr 3=2 \@attr 4=1 \@attr 5=1 ";#Firstinfield Phrase, Right truncated >- } else { >- $attr .=" \@attr 5=1 \@attr 4=6 ";## Word list, right truncated, anywhere >+ elsif ( @$tags[$i] eq "mainentry" ) { >+ $attr = " \@attr 1=Heading "; >+ } >+ elsif ( @$tags[$i] eq "any" ) { >+ $attr = " \@attr 1=Any "; >+ } >+ elsif ( @$tags[$i] eq "match" ) { >+ $attr = " \@attr 1=Match "; >+ } >+ elsif ( @$tags[$i] eq "match-heading" ) { >+ $attr = " \@attr 1=Match-heading "; >+ } >+ elsif ( @$tags[$i] eq "see-from" ) { >+ $attr = " \@attr 1=Match-heading-see-from "; >+ } >+ elsif ( @$tags[$i] eq "thesaurus" ) { >+ $attr = " \@attr 1=Subject-heading-thesaurus "; >+ } >+ if ( @$operator[$i] eq 'is' ) { >+ $attr .= " \@attr 4=1 \@attr 5=100 " >+ ; ##Phrase, No truncation,all of subfield field must match >+ } >+ elsif ( @$operator[$i] eq "=" ) { >+ $attr .= " \@attr 4=107 "; #Number Exact match >+ } >+ elsif ( @$operator[$i] eq "start" ) { >+ $attr .= " \@attr 3=2 \@attr 4=1 \@attr 5=1 " >+ ; #Firstinfield Phrase, Right truncated >+ } >+ elsif ( @$operator[$i] eq "exact" ) { >+ $attr .= " \@attr 4=1 \@attr 5=100 \@attr 6=3 " >+ ; ##Phrase, No truncation,all of subfield field must match >+ } >+ else { >+ $attr .= " \@attr 5=1 \@attr 4=6 " >+ ; ## Word list, right truncated, anywhere > } > @$value[$i] =~ s/"/\\"/g; # Escape the double-quotes in the search value > $attr =$attr."\"".@$value[$i]."\""; >@@ -251,8 +272,8 @@ sub SearchAuthorities { > }#if value > } > ##Add how many queries generated >- if ($query=~/\S+/){ >- $query= $and x $attr_cnt . $query . $q2; >+ if (defined $query && $query=~/\S+/){ >+ $query= $and x $attr_cnt . $query . (defined $q2 ? $q2 : ''); > } else { > $query= $q2; > } >@@ -309,35 +330,42 @@ sub SearchAuthorities { > my $separator=C4::Context->preference('authoritysep'); > $authrecord = MARC::File::USMARC::decode($marcdata); > my $authid=$authrecord->field('001')->data(); >- my $summary=BuildSummary($authrecord,$authid,$authtypecode); >- my $query_auth_tag = "SELECT auth_tag_to_report FROM auth_types WHERE authtypecode=?"; >- my $sth = $dbh->prepare($query_auth_tag); >- $sth->execute($authtypecode); >- my $auth_tag_to_report = $sth->fetchrow; >- my $reported_tag; >- my $mainentry = $authrecord->field($auth_tag_to_report); >- if ($mainentry) { >- foreach ($mainentry->subfields()) { >- $reported_tag .='$'.$_->[0].$_->[1]; >- } >- } > my %newline; >- $newline{summary} = $summary; > $newline{authid} = $authid; >- $newline{even} = $counter % 2; >- $newline{reported_tag} = $reported_tag; >+ if ( !$skipmetadata ) { >+ my $summary = >+ BuildSummary( $authrecord, $authid, $authtypecode ); >+ my $query_auth_tag = >+"SELECT auth_tag_to_report FROM auth_types WHERE authtypecode=?"; >+ my $sth = $dbh->prepare($query_auth_tag); >+ $sth->execute($authtypecode); >+ my $auth_tag_to_report = $sth->fetchrow; >+ my $reported_tag; >+ my $mainentry = $authrecord->field($auth_tag_to_report); >+ if ($mainentry) { >+ >+ foreach ( $mainentry->subfields() ) { >+ $reported_tag .= '$' . $_->[0] . $_->[1]; >+ } >+ } >+ $newline{summary} = $summary; >+ $newline{even} = $counter % 2; >+ $newline{reported_tag} = $reported_tag; >+ } > $counter++; > push @finalresult, \%newline; > }## while counter >- ### >- for (my $z=0; $z<@finalresult; $z++){ >- my $count=CountUsage($finalresult[$z]{authid}); >- $finalresult[$z]{used}=$count; >- }# all $z's >- >+ ### >+ if (! $skipmetadata) { >+ for (my $z=0; $z<@finalresult; $z++){ >+ my $count=CountUsage($finalresult[$z]{authid}); >+ $finalresult[$z]{used}=$count; >+ }# all $z's >+ } >+ > }## if nbresult > NOLUCK: >- # $oAResult->destroy(); >+ $oAResult->destroy(); > # $oAuth[0]->destroy(); > > return (\@finalresult, $nbresults); >@@ -854,53 +882,6 @@ sub GetAuthType { > } > > >-sub AUTHhtml2marc { >- my ($rtags,$rsubfields,$rvalues,%indicators) = @_; >- my $dbh=C4::Context->dbh; >- my $prevtag = -1; >- my $record = MARC::Record->new(); >-#---- TODO : the leader is missing >- >-# my %subfieldlist=(); >- my $prevvalue; # if tag <10 >- my $field; # if tag >=10 >- for (my $i=0; $i< @$rtags; $i++) { >- # rebuild MARC::Record >- if (@$rtags[$i] ne $prevtag) { >- if ($prevtag < 10) { >- if ($prevvalue) { >- $record->add_fields((sprintf "%03s",$prevtag),$prevvalue); >- } >- } else { >- if ($field) { >- $record->add_fields($field); >- } >- } >- $indicators{@$rtags[$i]}.=' '; >- if (@$rtags[$i] <10) { >- $prevvalue= @$rvalues[$i]; >- undef $field; >- } else { >- undef $prevvalue; >- $field = MARC::Field->new( (sprintf "%03s",@$rtags[$i]), substr($indicators{@$rtags[$i]},0,1),substr($indicators{@$rtags[$i]},1,1), @$rsubfields[$i] => @$rvalues[$i]); >- } >- $prevtag = @$rtags[$i]; >- } else { >- if (@$rtags[$i] <10) { >- $prevvalue=@$rvalues[$i]; >- } else { >- if (length(@$rvalues[$i])>0) { >- $field->add_subfields(@$rsubfields[$i] => @$rvalues[$i]); >- } >- } >- $prevtag= @$rtags[$i]; >- } >- } >- # the last has not been included inside the loop... do it now ! >- $record->add_fields($field) if $field; >- return $record; >-} >- > =head2 FindDuplicateAuthority > > $record= &FindDuplicateAuthority( $record, $authtypecode) >@@ -1015,14 +996,14 @@ sub BuildSummary{ > $resultstring =~ s/\n/<br>/g; > $summary = $resultstring; > } else { >- my $heading; >- my $altheading; >- my $seealso; >- my $broaderterms; >- my $narrowerterms; >- my $see; >- my $seeheading; >- my $notes; >+ my $heading = ''; >+ my $altheading = ''; >+ my $seealso = ''; >+ my $broaderterms = ''; >+ my $narrowerterms = ''; >+ my $see = ''; >+ my $seeheading = ''; >+ my $notes = ''; > my @fields = $record->fields(); > if (C4::Context->preference('marcflavour') eq 'UNIMARC') { > # construct UNIMARC summary, that is quite different from MARC21 one >@@ -1471,7 +1452,7 @@ sub get_auth_type_location { > my $auth_type_code = @_ ? shift : ''; > > my ($tag, $subfield) = GetAuthMARCFromKohaField('auth_header.authtypecode', $auth_type_code); >- if (defined $tag and defined $subfield and $tag != 0 and $subfield != 0) { >+ if (defined $tag and defined $subfield and $tag != 0 and $subfield ne '' and $subfield ne ' ') { > return ($tag, $subfield); > } else { > if (C4::Context->preference('marcflavour') eq "MARC21") { >diff --git a/C4/Biblio.pm b/C4/Biblio.pm >index ad559fa..58aa300 100644 >--- a/C4/Biblio.pm >+++ b/C4/Biblio.pm >@@ -35,6 +35,7 @@ use C4::Dates qw/format_date/; > use C4::Log; # logaction > use C4::ClassSource; > use C4::Charset; >+use C4::Linker; > require C4::Heading; > require C4::Serials; > require C4::Items; >@@ -114,6 +115,7 @@ BEGIN { > # To link headings in a bib record > # to authority records. > push @EXPORT, qw( >+ &BiblioAutoLink > &LinkBibHeadingsToAuthorities > ); > >@@ -457,9 +459,41 @@ sub DelBiblio { > return; > } > >+ >+=head2 BiblioAutoLink >+ >+ my $headings_linked = BiblioAutoLink($record, $frameworkcode) >+ >+Automatically links headings in a bib record to authorities. >+ >+=cut >+ >+sub BiblioAutoLink { >+ my $record = shift; >+ my $frameworkcode = shift; >+ my ( $num_headings_changed, %results ); >+ >+ my $linker_module = >+ "C4::Linker::" . ( C4::Context->preference("LinkerModule") || 'Default' ); >+ eval { eval "require $linker_module"; }; >+ if ($@) { >+ $linker_module = 'C4::Linker::Default'; >+ eval "require $linker_module"; >+ } >+ if ($@) { >+ return 0, 0; >+ } >+ >+ my $linker = $linker_module->new( >+ { 'options' => C4::Context->preference("LinkerOptions") } ); >+ my ( $headings_changed, undef ) = >+ LinkBibHeadingsToAuthorities( $linker, $record, $frameworkcode ); >+ return $headings_changed; >+} >+ > =head2 LinkBibHeadingsToAuthorities > >- my $headings_linked = LinkBibHeadingsToAuthorities($marc); >+ my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode); > > Links bib headings to authority records by checking > each authority-controlled field in the C<MARC::Record> >@@ -477,37 +511,109 @@ MARC record. > =cut > > sub LinkBibHeadingsToAuthorities { >- my $bib = shift; >+ my $linker = shift; >+ my $bib = shift; >+ my $frameworkcode = shift; >+ my %results; >+ require C4::AuthoritiesMarc; > > my $num_headings_changed = 0; > foreach my $field ( $bib->fields() ) { >- my $heading = C4::Heading->new_from_bib_field($field); >+ my $heading = C4::Heading->new_from_bib_field( $field, $frameworkcode ); > next unless defined $heading; > > # check existing $9 > my $current_link = $field->subfield('9'); > >- # look for matching authorities >- my $authorities = $heading->authorities(); >+ if ( defined $current_link && !C4::Context->preference('LinkerRelink') ) >+ { >+ $results{'linked'}->{ $heading->display_form() }++; >+ next; >+ } > >- # want only one exact match >- if ( $#{$authorities} == 0 ) { >- my $authority = MARC::Record->new_from_usmarc( $authorities->[0] ); >- my $authid = $authority->field('001')->data(); >- next if defined $current_link and $current_link eq $authid; >+ my ( $authid, $fuzzy ) = $linker->get_link($heading); >+ if ($authid) { >+ $results{ $fuzzy ? 'fuzzy' : 'linked' } >+ ->{ $heading->display_form() }++; >+ next if defined $current_link and $current_link == $authid; > > $field->delete_subfield( code => '9' ) if defined $current_link; > $field->add_subfields( '9', $authid ); > $num_headings_changed++; >- } else { >- if ( defined $current_link ) { >+ } >+ else { >+ if ( defined $current_link >+ && C4::Context->preference('LinkerKeepStale') ) >+ { >+ $results{'fuzzy'}->{ $heading->display_form() }++; >+ } >+ elsif ( C4::Context->preference('AutoCreateAuthorities') ) { >+ my $authtypedata = >+ C4::AuthoritiesMarc::GetAuthType( $heading->auth_type() ); >+ my $marcrecordauth = MARC::Record->new(); >+ if ( C4::Context->preference('marcflavour') eq 'MARC21' ) { >+ $marcrecordauth->leader(' nz a22 o 4500'); >+ SetMarcUnicodeFlag( $marcrecordauth, 'MARC21' ); >+ } >+ my $authfield = >+ MARC::Field->new( $authtypedata->{auth_tag_to_report}, >+ '', '', "a" => "" . $field->subfield('a') ); >+ map { >+ $authfield->add_subfields( $_->[0] => $_->[1] ) >+ if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" ) >+ } $field->subfields(); >+ $marcrecordauth->insert_fields_ordered($authfield); >+ >+# bug 2317: ensure new authority knows it's using UTF-8; currently >+# only need to do this for MARC21, as MARC::Record->as_xml_record() handles >+# automatically for UNIMARC (by not transcoding) >+# FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record >+# use UTF-8, but as of 2008-08-05, did not want to introduce that kind >+# of change to a core API just before the 3.0 release. >+ >+ if ( C4::Context->preference('marcflavour') eq 'MARC21' ) { >+ $marcrecordauth->insert_fields_ordered( >+ MARC::Field->new( >+ '667', '', '', >+ 'a' => "Machine generated authority record." >+ ) >+ ); >+ my $cite = >+ $bib->author() . ", " >+ . $bib->title_proper() . ", " >+ . $bib->publication_date() . " "; >+ $cite =~ s/^[\s\,]*//; >+ $cite =~ s/[\s\,]*$//; >+ $cite = >+ "Work cat.: (" >+ . C4::Context->preference('MARCOrgCode') . ")" >+ . $bib->subfield( '999', 'c' ) . ": " >+ . $cite; >+ $marcrecordauth->insert_fields_ordered( >+ MARC::Field->new( '670', '', '', 'a' => $cite ) ); >+ } >+ >+ # warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted; >+ >+ $authid = >+ C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '', >+ $heading->auth_type() ); >+ $field->add_subfields( '9', $authid ); >+ $num_headings_changed++; >+ $results{'added'}->{ $heading->display_form() }++; >+ } >+ elsif ( defined $current_link ) { > $field->delete_subfield( code => '9' ); > $num_headings_changed++; >+ $results{'unlinked'}->{ $heading->display_form() }++; >+ } >+ else { >+ $results{'unlinked'}->{ $heading->display_form() }++; > } > } > > } >- return $num_headings_changed; >+ return $num_headings_changed, \%results; > } > > =head2 GetRecordValue >diff --git a/C4/Heading.pm b/C4/Heading.pm >index ba9d23f..33e7913 100644 >--- a/C4/Heading.pm >+++ b/C4/Heading.pm >@@ -1,7 +1,7 @@ > package C4::Heading; > > # Copyright (C) 2008 LibLime >-# >+# > # This file is part of Koha. > # > # Koha is free software; you can redistribute it and/or modify it under the >@@ -18,12 +18,12 @@ package C4::Heading; > # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > > use strict; >-#use warnings; FIXME - Bug 2505 >+use warnings; > use MARC::Record; > use MARC::Field; > use C4::Context; >-use C4::Heading::MARC21; >-use C4::Search; >+use C4::AuthoritiesMarc; >+use Module::Load; > use Carp; > > our $VERSION = 3.00; >@@ -35,11 +35,11 @@ C4::Heading > =head1 SYNOPSIS > > use C4::Heading; >- my $heading = C4::Heading->new_from_bib_field($field); >+ my $heading = C4::Heading->new_from_bib_field($field, $frameworkcode); > my $thesaurus = $heading->thesaurus(); > my $type = $heading->type(); >- my $display_heading = $heading->display(); >- my $search_string = $heading->search_string(); >+ my $display_heading = $heading->display_form(); >+ my $search_form = $heading->search_form(); > > =head1 DESCRIPTION > >@@ -50,7 +50,7 @@ headings found in bibliographic and authority records. > > =head2 new_from_bib_field > >- my $heading = C4::Heading->new_from_bib_field($field[, $marc_flavour]); >+ my $heading = C4::Heading->new_from_bib_field($field, $frameworkcode, [, $marc_flavour]); > > Given a C<MARC::Field> object containing a heading from a > bib record, create a C<C4::Heading> object. >@@ -65,24 +65,54 @@ is returned. > =cut > > sub new_from_bib_field { >- my $class = shift; >- my $field = shift; >- my $marcflavour = @_ ? shift : C4::Context->preference('marcflavour'); >+ my $class = shift; >+ my $field = shift; >+ my $frameworkcode = shift; >+ my $marcflavour = @_ ? shift : C4::Context->preference('marcflavour'); > > my $marc_handler = _marc_format_handler($marcflavour); > > my $tag = $field->tag(); >- return unless $marc_handler->valid_bib_heading_tag($tag); >+ return unless $marc_handler->valid_bib_heading_tag( $tag, $frameworkcode ); > my $self = {}; >- >- ($self->{'auth_type'}, $self->{'subject_added_entry'}, $self->{'series_added_entry'}, $self->{'main_entry'}, >- $self->{'thesaurus'}, $self->{'search_form'}, $self->{'display_form'}) = >- $marc_handler->parse_heading($field); >+ >+ $self->{'field'} = $field; >+ ( >+ $self->{'auth_type'}, $self->{'thesaurus'}, >+ $self->{'search_form'}, $self->{'display_form'}, >+ $self->{'match_type'} >+ ) = $marc_handler->parse_heading($field); > > bless $self, $class; > return $self; > } > >+=head2 auth_type >+ >+ my $auth_type = $heading->auth_type(); >+ >+Return the auth_type of the heading. >+ >+=cut >+ >+sub auth_type { >+ my $self = shift; >+ return $self->{'auth_type'}; >+} >+ >+=head2 field >+ >+ my $field = $heading->field(); >+ >+Return the MARC::Field the heading is based on. >+ >+=cut >+ >+sub field { >+ my $self = shift; >+ return $self->{'field'}; >+} >+ > =head2 display_form > > my $display = $heading->display_form(); >@@ -96,23 +126,33 @@ sub display_form { > return $self->{'display_form'}; > } > >+=head2 search_form >+ >+ my $search_form = $heading->search_form(); >+ >+Return the "canonical" search form of the heading. >+ >+=cut >+ >+sub search_form { >+ my $self = shift; >+ return $self->{'search_form'}; >+} >+ > =head2 authorities > >- my $authorities = $heading->authorities; >+ my $authorities = $heading->authorities([$skipmetadata]); > > Return a list of authority records for this >-heading. >+heading. If passed a true value for $skipmetadata, >+SearchAuthorities will return only authids. > > =cut > > sub authorities { >- my $self = shift; >- my $query = qq(Match-heading,do-not-truncate,ext="$self->{'search_form'}"); >- $query .= $self->_query_limiters(); >- my ($error, $results, $total_hits) = SimpleSearch( $query, undef, undef, [ "authorityserver" ] ); >- if (defined $error) { >- carp "Error:$error from search $query"; >- } >+ my $self = shift; >+ my $skipmetadata = shift; >+ my ( $results, $total ) = _search( $self, 'match-heading', $skipmetadata ); > return $results; > } > >@@ -127,36 +167,46 @@ that are a preferred form of the heading. > > sub preferred_authorities { > my $self = shift; >- my $query = "Match-heading-see-from,do-not-truncate,ext='$self->{'search_form'}'"; >- $query .= $self->_query_limiters(); >- my ($error, $results, $total_hits) = SimpleSearch( $query, undef, undef, [ "authorityserver" ] ); >- if (defined $error) { >- carp "Error:$error from search $query"; >- } >+ my $skipmetadata = shift || undef; >+ my ( $results, $total ) = _search( 'see-from', $skipmetadata ); > return $results; > } > > =head1 INTERNAL METHODS > >-=head2 _query_limiters >+=head2 _search > > =cut > >-sub _query_limiters { >- my $self = shift; >- >- my $limiters = " AND at='$self->{'auth_type'}'"; >- if ($self->{'subject_added_entry'}) { >- $limiters .= " AND Heading-use-subject-added-entry=a"; # FIXME -- is this properly in C4::Heading::MARC21? >- $limiters .= " AND Subject-heading-thesaurus=$self->{'thesaurus'}"; >- } >- if ($self->{'series_added_entry'}) { >- $limiters .= " AND Heading-use-series-added-entry=a"; # FIXME -- is this properly in C4::Heading::MARC21? >- } >- if (not $self->{'subject_added_entry'} and not $self->{'series_added_entry'}) { >- $limiters .= " AND Heading-use-main-or-added-entry=a" # FIXME -- is this properly in C4::Heading::MARC21? >+sub _search { >+ my $self = shift; >+ my $index = shift || undef; >+ my $skipmetadata = shift || undef; >+ my @marclist; >+ my @and_or; >+ my @excluding = []; >+ my @operator; >+ my @value; >+ >+ if ($index) { >+ push @marclist, $index; >+ push @and_or, 'and'; >+ push @operator, $self->{'match_type'}; >+ push @value, $self->{'search_form'}; > } >- return $limiters; >+ >+ # if ($self->{'thesaurus'}) { >+ # push @marclist, 'thesaurus'; >+ # push @and_or, 'and'; >+ # push @excluding, ''; >+ # push @operator, 'is'; >+ # push @value, $self->{'thesaurus'}; >+ # } >+ return SearchAuthorities( >+ \@marclist, \@and_or, \@excluding, \@operator, >+ \@value, 0, 20, $self->{'auth_type'}, >+ '', $skipmetadata >+ ); > } > > =head1 INTERNAL FUNCTIONS >@@ -169,14 +219,11 @@ depending on the selected MARC flavour. > =cut > > sub _marc_format_handler { >- my $marcflavour = shift; >- >- if ($marcflavour eq 'UNIMARC') { >- return C4::Heading::UNIMARC->new(); >- } else { >- return C4::Heading::MARC21->new(); >- } >- >+ my $marcflavour = uc shift; >+ $marcflavour = 'MARC21' if ( $marcflavour eq 'NORMARC' ); >+ my $pname = "C4::Heading::$marcflavour"; >+ load $pname; >+ return $pname->new(); > } > > =head1 AUTHOR >diff --git a/C4/Heading/MARC21.pm b/C4/Heading/MARC21.pm >index 77cdac2..d873e8d 100644 >--- a/C4/Heading/MARC21.pm >+++ b/C4/Heading/MARC21.pm >@@ -1,7 +1,7 @@ > package C4::Heading::MARC21; > > # Copyright (C) 2008 LibLime >-# >+# > # This file is part of Koha. > # > # Koha is free software; you can redistribute it and/or modify it under the >@@ -18,7 +18,7 @@ package C4::Heading::MARC21; > # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > > use strict; >-#use warnings; FIXME - Bug 2505 >+use warnings; > use MARC::Record; > use MARC::Field; > >@@ -49,27 +49,69 @@ FIXME - this should be moved to a configuration file. > =cut > > my $bib_heading_fields = { >- '100' => { auth_type => 'PERSO_NAME', subfields => 'abcdefghjklmnopqrst', main_entry => 1 }, >- '110' => { auth_type => 'CORPO_NAME', subfields => 'abcdefghklmnoprst', main_entry => 1 }, >- '111' => { auth_type => 'MEETI_NAME', subfields => 'acdefghjklnpqst', main_entry => 1 }, >- '130' => { auth_type => 'UNIF_TITLE', subfields => 'adfghklmnoprst', main_entry => 1 }, >+ '100' => { >+ auth_type => 'PERSO_NAME', >+ subfields => 'abcdfghjklmnopqrst', >+ main_entry => 1 >+ }, >+ '110' => { >+ auth_type => 'CORPO_NAME', >+ subfields => 'abcdfghklmnoprst', >+ main_entry => 1 >+ }, >+ '111' => { >+ auth_type => 'MEETI_NAME', >+ subfields => 'acdfghjklnpqst', >+ main_entry => 1 >+ }, >+ '130' => { >+ auth_type => 'UNIF_TITLE', >+ subfields => 'adfghklmnoprst', >+ main_entry => 1 >+ }, > '440' => { auth_type => 'UNIF_TITLE', subfields => 'anp', series => 1 }, >- '600' => { auth_type => 'PERSO_NAME', subfields => 'abcdefghjklmnopqrstvxyz', subject => 1 }, >- '610' => { auth_type => 'CORPO_NAME', subfields => 'abcdefghklmnoprstvxyz', subject => 1 }, >- '611' => { auth_type => 'MEETI_NAME', subfields => 'acdefghjklnpqstvxyz', subject => 1 }, >- '630' => { auth_type => 'UNIF_TITLE', subfields => 'adfghklmnoprstvxyz', subject => 1 }, >- '648' => { auth_type => 'CHRON_TERM', subfields => 'avxyz', subject => 1 }, >+ '600' => { >+ auth_type => 'PERSO_NAME', >+ subfields => 'abcdfghjklmnopqrstvxyz', >+ subject => 1 >+ }, >+ '610' => { >+ auth_type => 'CORPO_NAME', >+ subfields => 'abcdfghklmnoprstvxyz', >+ subject => 1 >+ }, >+ '611' => { >+ auth_type => 'MEETI_NAME', >+ subfields => 'acdfghjklnpqstvxyz', >+ subject => 1 >+ }, >+ '630' => { >+ auth_type => 'UNIF_TITLE', >+ subfields => 'adfghklmnoprstvxyz', >+ subject => 1 >+ }, >+ '648' => { auth_type => 'CHRON_TERM', subfields => 'avxyz', subject => 1 }, > '650' => { auth_type => 'TOPIC_TERM', subfields => 'abvxyz', subject => 1 }, >- '651' => { auth_type => 'GEOGR_NAME', subfields => 'avxyz', subject => 1 }, >- '655' => { auth_type => 'GENRE/FORM', subfields => 'avxyz', subject => 1 }, >- '700' => { auth_type => 'PERSO_NAME', subfields => 'abcdefghjklmnopqrst' }, >- '710' => { auth_type => 'CORPO_NAME', subfields => 'abcdefghklmnoprst' }, >- '711' => { auth_type => 'MEETI_NAME', subfields => 'acdefghjklnpqst' }, >+ '651' => { auth_type => 'GEOGR_NAME', subfields => 'avxyz', subject => 1 }, >+ '655' => { auth_type => 'GENRE/FORM', subfields => 'avxyz', subject => 1 }, >+ '700' => { auth_type => 'PERSO_NAME', subfields => 'abcdfghjklmnopqrst' }, >+ '710' => { auth_type => 'CORPO_NAME', subfields => 'abcdfghklmnoprst' }, >+ '711' => { auth_type => 'MEETI_NAME', subfields => 'acdfghjklnpqst' }, > '730' => { auth_type => 'UNIF_TITLE', subfields => 'adfghklmnoprst' }, >- '800' => { auth_type => 'PERSO_NAME', subfields => 'abcdefghjklmnopqrst', series => 1 }, >- '810' => { auth_type => 'CORPO_NAME', subfields => 'abcdefghklmnoprst', series => 1 }, >- '811' => { auth_type => 'MEETI_NAME', subfields => 'acdefghjklnpqst', series => 1 }, >- '830' => { auth_type => 'UNIF_TITLE', subfields => 'adfghklmnoprst', series => 1 }, >+ '800' => { >+ auth_type => 'PERSO_NAME', >+ subfields => 'abcdfghjklmnopqrst', >+ series => 1 >+ }, >+ '810' => { >+ auth_type => 'CORPO_NAME', >+ subfields => 'abcdfghklmnoprst', >+ series => 1 >+ }, >+ '811' => >+ { auth_type => 'MEETI_NAME', subfields => 'acdfghjklnpqst', series => 1 }, >+ '830' => >+ { auth_type => 'UNIF_TITLE', subfields => 'adfghklmnoprst', series => 1 }, > }; > > =head2 subdivisions >@@ -101,12 +143,14 @@ sub new { > =cut > > sub valid_bib_heading_tag { >- my $self = shift; >- my $tag = shift; >+ my $self = shift; >+ my $tag = shift; >+ my $frameworkcode = shift; > >- if (exists $bib_heading_fields->{$tag}) { >- return 1 >- } else { >+ if ( exists $bib_heading_fields->{$tag} ) { >+ return 1; >+ } >+ else { > return 0; > } > >@@ -117,21 +161,24 @@ sub valid_bib_heading_tag { > =cut > > sub parse_heading { >- my $self = shift; >+ my $self = shift; > my $field = shift; > >- my $tag = $field->tag; >+ my $tag = $field->tag; > my $field_info = $bib_heading_fields->{$tag}; > > my $auth_type = $field_info->{'auth_type'}; >- my $subject = $field_info->{'subject'} ? 1 : 0; >- my $series = $field_info->{'series'} ? 1 : 0; >- my $main_entry = $field_info->{'main_entry'} ? 1 : 0; >- my $thesaurus = $subject ? _get_subject_thesaurus($field) : "lcsh"; # use 'lcsh' for names, UT, etc. >- my $search_heading = _get_search_heading($field, $field_info->{'subfields'}); >- my $display_heading = _get_display_heading($field, $field_info->{'subfields'}); >- >- return ($auth_type, $subject, $series, $main_entry, $thesaurus, $search_heading, $display_heading); >+ my $thesaurus = >+ $tag =~ m/6../ >+ ? _get_subject_thesaurus($field) >+ : "lcsh"; # use 'lcsh' for names, UT, etc. >+ my $search_heading = >+ _get_search_heading( $field, $field_info->{'subfields'} ); >+ my $display_heading = >+ _get_display_heading( $field, $field_info->{'subfields'} ); >+ >+ return ( $auth_type, $thesaurus, $search_heading, $display_heading, >+ 'exact' ); > } > > =head1 INTERNAL FUNCTIONS >@@ -142,24 +189,31 @@ sub parse_heading { > > sub _get_subject_thesaurus { > my $field = shift; >- my $ind2 = $field->indicator(2); >+ my $ind2 = $field->indicator(2); > > my $thesaurus = "notdefined"; >- if ($ind2 eq '0') { >+ if ( $ind2 eq '0' ) { > $thesaurus = "lcsh"; >- } elsif ($ind2 eq '1') { >+ } >+ elsif ( $ind2 eq '1' ) { > $thesaurus = "lcac"; >- } elsif ($ind2 eq '2') { >+ } >+ elsif ( $ind2 eq '2' ) { > $thesaurus = "mesh"; >- } elsif ($ind2 eq '3') { >+ } >+ elsif ( $ind2 eq '3' ) { > $thesaurus = "nal"; >- } elsif ($ind2 eq '4') { >+ } >+ elsif ( $ind2 eq '4' ) { > $thesaurus = "notspecified"; >- } elsif ($ind2 eq '5') { >+ } >+ elsif ( $ind2 eq '5' ) { > $thesaurus = "cash"; >- } elsif ($ind2 eq '6') { >+ } >+ elsif ( $ind2 eq '6' ) { > $thesaurus = "rvm"; >- } elsif ($ind2 eq '7') { >+ } >+ elsif ( $ind2 eq '7' ) { > my $sf2 = $field->subfield('2'); > $thesaurus = $sf2 if defined($sf2); > } >@@ -172,24 +226,27 @@ sub _get_subject_thesaurus { > =cut > > sub _get_search_heading { >- my $field = shift; >+ my $field = shift; > my $subfields = shift; > >- my $heading = ""; >+ my $heading = ""; > my @subfields = $field->subfields(); >- my $first = 1; >- for (my $i = 0; $i <= $#subfields; $i++) { >- my $code = $subfields[$i]->[0]; >+ my $first = 1; >+ for ( my $i = 0 ; $i <= $#subfields ; $i++ ) { >+ my $code = $subfields[$i]->[0]; > my $code_re = quotemeta $code; >- my $value = $subfields[$i]->[1]; >+ my $value = $subfields[$i]->[1]; >+ $value =~ s/[-,.:=;!%\/]$//; > next unless $subfields =~ qr/$code_re/; > if ($first) { >- $first = 0; >+ $first = 0; > $heading = $value; >- } else { >- if (exists $subdivisions{$code}) { >+ } >+ else { >+ if ( exists $subdivisions{$code} ) { > $heading .= " $subdivisions{$code} $value"; >- } else { >+ } >+ else { > $heading .= " $value"; > } > } >@@ -206,24 +263,26 @@ sub _get_search_heading { > =cut > > sub _get_display_heading { >- my $field = shift; >+ my $field = shift; > my $subfields = shift; > >- my $heading = ""; >+ my $heading = ""; > my @subfields = $field->subfields(); >- my $first = 1; >- for (my $i = 0; $i <= $#subfields; $i++) { >- my $code = $subfields[$i]->[0]; >+ my $first = 1; >+ for ( my $i = 0 ; $i <= $#subfields ; $i++ ) { >+ my $code = $subfields[$i]->[0]; > my $code_re = quotemeta $code; >- my $value = $subfields[$i]->[1]; >+ my $value = $subfields[$i]->[1]; > next unless $subfields =~ qr/$code_re/; > if ($first) { >- $first = 0; >+ $first = 0; > $heading = $value; >- } else { >- if (exists $subdivisions{$code}) { >+ } >+ else { >+ if ( exists $subdivisions{$code} ) { > $heading .= "--$value"; >- } else { >+ } >+ else { > $heading .= " $value"; > } > } >@@ -231,6 +290,17 @@ sub _get_display_heading { > return $heading; > } > >+# Additional limiters that we aren't using: >+# if ($self->{'subject_added_entry'}) { >+# $limiters .= " AND Heading-use-subject-added-entry=a"; >+# } >+# if ($self->{'series_added_entry'}) { >+# $limiters .= " AND Heading-use-series-added-entry=a"; >+# } >+# if (not $self->{'subject_added_entry'} and not $self->{'series_added_entry'}) { >+# $limiters .= " AND Heading-use-main-or-added-entry=a" >+# } >+ > =head1 AUTHOR > > Koha Development Team <http://koha-community.org/> >diff --git a/C4/Heading/UNIMARC.pm b/C4/Heading/UNIMARC.pm >new file mode 100644 >index 0000000..78536b8 >--- /dev/null >+++ b/C4/Heading/UNIMARC.pm >@@ -0,0 +1,207 @@ >+package C4::Heading::UNIMARC; >+ >+# Copyright (C) 2011 C & P Bibliography Services >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use 5.010; >+use strict; >+use warnings; >+use MARC::Record; >+use MARC::Field; >+use C4::Context; >+ >+our $VERSION = 3.00; >+ >+=head1 NAME >+ >+C4::Heading::UNIMARC >+ >+=head1 SYNOPSIS >+ >+use C4::Heading::UNIMARC; >+ >+=head1 DESCRIPTION >+ >+This is an internal helper class used by >+C<C4::Heading> to parse headings data from >+UNIMARC records. Object of this type >+do not carry data, instead, they only >+dispatch functions. >+ >+=head1 DATA STRUCTURES >+ >+FIXME - this should be moved to a configuration file. >+ >+=head2 subdivisions >+ >+=cut >+ >+my %subdivisions = ( >+ 'j' => 'formsubdiv', >+ 'x' => 'generalsubdiv', >+ 'y' => 'chronologicalsubdiv', >+ 'z' => 'geographicsubdiv', >+); >+ >+my $bib_heading_fields; >+ >+BEGIN { >+ my $dbh = C4::Context->dbh; >+ my $sth = $dbh->prepare( >+ "SELECT tagfield, authtypecode >+ FROM marc_subfield_structure >+ WHERE frameworkcode = '' AND authtypecode <> ''" >+ ); >+ $sth->execute(); >+ $bib_heading_fields = {}; >+ while ( my ( $tag, $auth_type ) = $sth->fetchrow ) { >+ $bib_heading_fields->{$tag} = { >+ auth_type => $auth_type, >+ subfields => 'abcdefghjklmnopqrstvxyz', >+ }; >+ } >+} >+ >+=head1 METHODS >+ >+=head2 new >+ >+ my $marc_handler = C4::Heading::UNIMARC->new(); >+ >+=cut >+ >+sub new { >+ my $class = shift; >+ return bless {}, $class; >+} >+ >+=head2 valid_bib_heading_tag >+ >+=cut >+ >+sub valid_bib_heading_tag { >+ my ( $self, $tag ) = @_; >+ return $bib_heading_fields->{$tag}; >+} >+ >+=head2 parse_heading >+ >+=cut >+ >+sub parse_heading { >+ my ( $self, $field ) = @_; >+ >+ my $tag = $field->tag; >+ my $field_info = $bib_heading_fields->{$tag}; >+ my $auth_type = $field_info->{'auth_type'}; >+ my $search_heading = >+ _get_search_heading( $field, $field_info->{'subfields'} ); >+ my $display_heading = >+ _get_display_heading( $field, $field_info->{'subfields'} ); >+ >+ return ( $auth_type, undef, $search_heading, $display_heading, 'exact' ); >+} >+ >+=head1 INTERNAL FUNCTIONS >+ >+=head2 _get_subject_thesaurus >+ >+=cut >+ >+sub _get_subject_thesaurus { >+ my $field = shift; >+ >+ my $thesaurus = "notdefined"; >+ my $sf2 = $field->subfield('2'); >+ $thesaurus = $sf2 if defined($sf2); >+ >+ return $thesaurus; >+} >+ >+=head2 _get_search_heading >+ >+=cut >+ >+sub _get_search_heading { >+ my $field = shift; >+ my $subfields = shift; >+ >+ my $heading = ""; >+ my @subfields = $field->subfields(); >+ my $first = 1; >+ for ( my $i = 0 ; $i <= $#subfields ; $i++ ) { >+ my $code = $subfields[$i]->[0]; >+ my $code_re = quotemeta $code; >+ my $value = $subfields[$i]->[1]; >+ $value =~ s/[-,.:=;!%\/]*$//; >+ next unless $subfields =~ qr/$code_re/; >+ if ($first) { >+ $first = 0; >+ $heading = $value; >+ } >+ else { >+ $heading .= " $value"; >+ } >+ } >+ >+ # remove characters that are part of CCL syntax >+ $heading =~ s/[)(=]//g; >+ >+ return $heading; >+} >+ >+=head2 _get_display_heading >+ >+=cut >+ >+sub _get_display_heading { >+ my $field = shift; >+ my $subfields = shift; >+ >+ my $heading = ""; >+ my @subfields = $field->subfields(); >+ my $first = 1; >+ for ( my $i = 0 ; $i <= $#subfields ; $i++ ) { >+ my $code = $subfields[$i]->[0]; >+ my $code_re = quotemeta $code; >+ my $value = $subfields[$i]->[1]; >+ next unless $subfields =~ qr/$code_re/; >+ if ($first) { >+ $first = 0; >+ $heading = $value; >+ } >+ else { >+ if ( exists $subdivisions{$code} ) { >+ $heading .= "--$value"; >+ } >+ else { >+ $heading .= " $value"; >+ } >+ } >+ } >+ return $heading; >+} >+ >+=head1 AUTHOR >+ >+Koha Development Team <http://koha-community.org/> >+ >+Jared Camins-Esakov <jcamins@cpbibliography.com> >+ >+=cut >+ >+1; >diff --git a/C4/Linker.pm b/C4/Linker.pm >new file mode 100644 >index 0000000..f030258 >--- /dev/null >+++ b/C4/Linker.pm >@@ -0,0 +1,123 @@ >+package C4::Linker; >+ >+# Copyright 2011 C & P Bibliography Services >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+=head1 NAME >+ >+C4::Linker - Base class for linking authorities to bibliographic records >+ >+=head1 SYNOPSIS >+ >+ use C4::Linker (%params ); >+ >+=head1 DESCRIPTION >+ >+Base class for C4::Linker::X. Subclasses need to provide the following methods >+ >+B<get_link ($field)> - return the authid for the authority that should be >+linked to the provided MARC::Field object, and a boolean to indicate whether >+the match is "fuzzy" (the semantics of "fuzzy" are up to the individual plugin). >+In order to handle authority limits, get_link should always end with: >+ return $self->SUPER::_handle_auth_limit($authid), $fuzzy; >+ >+B<flip_heading ($field)> - return a MARC::Field object with the heading flipped >+to the preferred form. >+ >+=head1 FUNCTIONS >+ >+=cut >+ >+use strict; >+use warnings; >+use Carp; >+use C4::Context; >+ >+use base qw(Class::Accessor); >+ >+__PACKAGE__->mk_accessors(qw( )); >+ >+sub new { >+ my $class = shift; >+ my $param = shift; >+ >+ my $self = {}; >+ >+ while ( my ( $key, $value ) = each %$param ) { >+ if ( $key eq 'auth_limit' && $value ) { >+ my $dbh = C4::Context->dbh; >+ my $sql = >+ "SELECT authid FROM auth_header WHERE $value ORDER BY authid ASC"; >+ my $sth = $dbh->prepare($sql); >+ $sth->execute(); >+ while ( my ($authid) = $sth->fetchrow_array() ) { >+ push @{ $self->{'auths_to_link'} }, $authid; >+ } >+ } >+ elsif ( $key eq 'options' && $value ) { >+ foreach my $opt ( split( /\|/, $value ) ) { >+ $self->{$opt} = 1; >+ } >+ } >+ elsif ($value) { >+ $self->{$key} = $value; >+ } >+ } >+ >+ bless $self, $class; >+ return $self; >+} >+ >+=head2 _handle_auth_limit >+ >+ return $self->SUPER::_handle_auth_limit($authid), $fuzzy; >+ >+=back >+ >+Function to be called by subclasses to handle authority record limits. >+ >+=cut >+ >+sub _handle_auth_limit { >+ my $self = shift; >+ my $authid = shift; >+ >+ if ( defined $self->{'auths_to_link'} && defined $authid && !grep { $_ == $authid } >+ @{ $self->{'auths_to_link'} } ) >+ { >+ undef $authid; >+ } >+ return $authid; >+} >+ >+=head2 EXPORT >+ >+None by default. >+ >+=head1 SEE ALSO >+ >+C4::Linker::Default >+ >+=head1 AUTHOR >+ >+Jared Camins-Esakov, C & P Bibliography Services, E<lt>jcamins@cpbibliography.comE<gt> >+ >+=cut >+ >+1; >+ >+__END__ >diff --git a/C4/Linker/Default.pm b/C4/Linker/Default.pm >new file mode 100644 >index 0000000..653dabd >--- /dev/null >+++ b/C4/Linker/Default.pm >@@ -0,0 +1,97 @@ >+package C4::Linker::Default; >+ >+# Copyright 2011 C & P Bibliography Services >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use strict; >+use warnings; >+use Carp; >+use MARC::Field; >+use C4::Heading; >+ >+use base qw(C4::Linker); >+ >+sub get_link { >+ my $self = shift; >+ my $heading = shift; >+ my $behavior = shift || 'default'; >+ my $search_form = $heading->search_form(); >+ my $authid; >+ my $fuzzy = 0; >+ >+ if ( $self->{'cache'}->{$search_form}->{'cached'} ) { >+ $authid = $self->{'cache'}->{$search_form}->{'authid'}; >+ $fuzzy = $self->{'cache'}->{$search_form}->{'fuzzy'}; >+ } >+ else { >+ >+ # look for matching authorities >+ my $authorities = $heading->authorities(1); # $skipmetadata = true >+ >+ if ( $behavior eq 'default' && $#{$authorities} == 0 ) { >+ $authid = $authorities->[0]->{'authid'}; >+ } >+ elsif ( $behavior eq 'first' && $#{$authorities} >= 0 ) { >+ $authid = $authorities->[0]->{'authid'}; >+ $fuzzy = $#{$authorities} > 0; >+ } >+ elsif ( $behavior eq 'last' && $#{$authorities} >= 0 ) { >+ $authid = $authorities->[ $#{$authorities} ]->{'authid'}; >+ $fuzzy = $#{$authorities} > 0; >+ } >+ >+ if ( !defined $authid && $self->{'broader_headings'} ) { >+ my $field = $heading->field(); >+ my @subfields = $field->subfields(); >+ if ( scalar @subfields > 1 ) { >+ pop @subfields; >+ $field->replace_with( >+ MARC::Field->new( >+ $field->tag, >+ $field->indicator(1), >+ $field->indicator(2), >+ map { $_[0] => $_[1] } @subfields >+ ) >+ ); >+ ( $authid, $fuzzy ) = >+ $self->get_link( C4::Heading->new_from_bib_field($field), >+ $behavior ); >+ } >+ } >+ >+ $self->{'cache'}->{$search_form}->{'cached'} = 1; >+ $self->{'cache'}->{$search_form}->{'authid'} = $authid; >+ $self->{'cache'}->{$search_form}->{'fuzzy'} = $fuzzy; >+ } >+ return $self->SUPER::_handle_auth_limit($authid), $fuzzy; >+} >+ >+sub flip_heading { >+ my $self = shift; >+ my $heading = shift; >+ >+ # TODO: implement >+} >+ >+1; >+__END__ >+ >+=head1 NAME >+ >+C4::Linker::Default - match only if there is a single matching auth >+ >+=cut >diff --git a/C4/Linker/FirstMatch.pm b/C4/Linker/FirstMatch.pm >new file mode 100644 >index 0000000..ed03d73 >--- /dev/null >+++ b/C4/Linker/FirstMatch.pm >@@ -0,0 +1,58 @@ >+package C4::Linker::FirstMatch; >+ >+# Copyright 2011 C & P Bibliography Services >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use strict; >+use warnings; >+use Carp; >+use C4::Heading; >+use C4::Linker::Default; # Use Default for flipping >+ >+use base qw(C4::Linker); >+ >+sub new { >+ my $class = shift; >+ my $param = shift; >+ >+ my $self = $class->SUPER::new($param); >+ $self->{'default_linker'} = C4::Linker::Default->new($param); >+ bless $self, $class; >+ return $self; >+} >+ >+sub get_link { >+ my $self = shift; >+ my $heading = shift; >+ return $self->{'default_linker'}->get_link( $heading, 'first' ); >+} >+ >+sub flip_heading { >+ my $self = shift; >+ my $heading = shift; >+ >+ return $self->{'default_linker'}->flip($heading); >+} >+ >+1; >+__END__ >+ >+=head1 NAME >+ >+C4::Linker::FirstMatch - match against the first authority record >+ >+=cut >diff --git a/C4/Linker/LastMatch.pm b/C4/Linker/LastMatch.pm >new file mode 100644 >index 0000000..9357e95 >--- /dev/null >+++ b/C4/Linker/LastMatch.pm >@@ -0,0 +1,58 @@ >+package C4::Linker::LastMatch; >+ >+# Copyright 2011 C & P Bibliography Services >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use strict; >+use warnings; >+use Carp; >+use C4::Heading; >+use C4::Linker::Default; # Use Default for flipping >+ >+use base qw(C4::Linker); >+ >+sub new { >+ my $class = shift; >+ my $param = shift; >+ >+ my $self = $class->SUPER::new($param); >+ $self->{'default_linker'} = C4::Linker::Default->new($param); >+ bless $self, $class; >+ return $self; >+} >+ >+sub get_link { >+ my $self = shift; >+ my $heading = shift; >+ return $self->{'default_linker'}->get_link( $heading, 'last' ); >+} >+ >+sub flip_heading { >+ my $self = shift; >+ my $heading = shift; >+ >+ return $self->{'default_linker'}->flip($heading); >+} >+ >+1; >+__END__ >+ >+=head1 NAME >+ >+C4::Linker::LastMatch - match against the last authority record >+ >+=cut >diff --git a/C4/Search.pm b/C4/Search.pm >index 4a8de5c..ba132be 100644 >--- a/C4/Search.pm >+++ b/C4/Search.pm >@@ -68,9 +68,7 @@ This module provides searching functions for Koha's bibliographic databases > &NZgetRecords > &AddSearchHistory > &GetDistinctValues >- &BiblioAddAuthorities > ); >-#FIXME: i had to add BiblioAddAuthorities here because in Biblios.pm it caused circular dependencies (C4::Search uses C4::Biblio, and BiblioAddAuthorities uses SimpleSearch from C4::Search) > > # make all your functions, whether exported or not; > >@@ -2638,105 +2636,6 @@ sub z3950_search_args { > return $array; > } > >-=head2 BiblioAddAuthorities >- >-( $countlinked, $countcreated ) = BiblioAddAuthorities($record, $frameworkcode); >- >-this function finds the authorities linked to the biblio >- * search in the authority DB for the same authid (in $9 of the biblio) >- * search in the authority DB for the same 001 (in $3 of the biblio in UNIMARC) >- * search in the authority DB for the same values (exactly) (in all subfields of the biblio) >-OR adds a new authority record >- >-=over 2 >- >-=item C<input arg:> >- >- * $record is the MARC record in question (marc blob) >- * $frameworkcode is the bibliographic framework to use (if it is "" it uses the default framework) >- >-=item C<Output arg:> >- >- * $countlinked is the number of authorities records that are linked to this authority >- * $countcreated >- >-=item C<BUGS> >- * I had to add this to Search.pm (instead of the logical Biblio.pm) because of a circular dependency (this sub uses SimpleSearch, and Search.pm uses Biblio.pm) >- >-=back >- >-=cut >- >- >-sub BiblioAddAuthorities{ >- my ( $record, $frameworkcode ) = @_; >- my $dbh=C4::Context->dbh; >- my $query=$dbh->prepare(qq| >-SELECT authtypecode,tagfield >-FROM marc_subfield_structure >-WHERE frameworkcode=? >-AND (authtypecode IS NOT NULL AND authtypecode<>\"\")|); >-# SELECT authtypecode,tagfield >-# FROM marc_subfield_structure >-# WHERE frameworkcode=? >-# AND (authtypecode IS NOT NULL OR authtypecode<>\"\")|); >- $query->execute($frameworkcode); >- my ($countcreated,$countlinked); >- while (my $data=$query->fetchrow_hashref){ >- foreach my $field ($record->field($data->{tagfield})){ >- next if ($field->subfield('3')||$field->subfield('9')); >- # No authorities id in the tag. >- # Search if there is any authorities to link to. >- my $query='at='.$data->{authtypecode}.' '; >- map {$query.= ' and he,ext="'.$_->[1].'"' if ($_->[0]=~/[A-z]/)} $field->subfields(); >- my ($error, $results, $total_hits)=SimpleSearch( $query, undef, undef, [ "authorityserver" ] ); >- # there is only 1 result >- if ( $error ) { >- warn "BIBLIOADDSAUTHORITIES: $error"; >- return (0,0) ; >- } >- if ( @{$results} == 1 ) { >- my $marcrecord = MARC::File::USMARC::decode($results->[0]); >- $field->add_subfields('9'=>$marcrecord->field('001')->data); >- $countlinked++; >- } elsif ( @{$results} > 1 ) { >- #More than One result >- #This can comes out of a lack of a subfield. >-# my $marcrecord = MARC::File::USMARC::decode($results->[0]); >-# $record->field($data->{tagfield})->add_subfields('9'=>$marcrecord->field('001')->data); >- $countlinked++; >- } else { >- #There are no results, build authority record, add it to Authorities, get authid and add it to 9 >- ###NOTICE : This is only valid if a subfield is linked to one and only one authtypecode >- ###NOTICE : This can be a problem. We should also look into other types and rejected forms. >- my $authtypedata=C4::AuthoritiesMarc::GetAuthType($data->{authtypecode}); >- next unless $authtypedata; >- my $marcrecordauth=MARC::Record->new(); >- my $authfield=MARC::Field->new($authtypedata->{auth_tag_to_report},'','',"a"=>"".$field->subfield('a')); >- map { $authfield->add_subfields($_->[0]=>$_->[1]) if ($_->[0]=~/[A-z]/ && $_->[0] ne "a" )} $field->subfields(); >- $marcrecordauth->insert_fields_ordered($authfield); >- >- # bug 2317: ensure new authority knows it's using UTF-8; currently >- # only need to do this for MARC21, as MARC::Record->as_xml_record() handles >- # automatically for UNIMARC (by not transcoding) >- # FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record >- # use UTF-8, but as of 2008-08-05, did not want to introduce that kind >- # of change to a core API just before the 3.0 release. >- if (C4::Context->preference('marcflavour') eq 'MARC21') { >- SetMarcUnicodeFlag($marcrecordauth, 'MARC21'); >- } >- >-# warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted; >- >- my $authid=AddAuthority($marcrecordauth,'',$data->{authtypecode}); >- $countcreated++; >- $field->add_subfields('9'=>$authid); >- } >- } >- } >- return ($countlinked,$countcreated); >-} >- > =head2 GetDistinctValues($field); > > C<$field> is a reference to the fields array >diff --git a/acqui/addorderiso2709.pl b/acqui/addorderiso2709.pl >index bbfd3b7..660d633 100755 >--- a/acqui/addorderiso2709.pl >+++ b/acqui/addorderiso2709.pl >@@ -33,7 +33,7 @@ use C4::Input; > use C4::Output; > use C4::ImportBatch; > use C4::Matcher; >-use C4::Search qw/FindDuplicate BiblioAddAuthorities/; >+use C4::Search qw/FindDuplicate/; > use C4::Acquisition; > use C4::Biblio; > use C4::Items; >@@ -183,7 +183,7 @@ if ($op eq ""){ > SetImportRecordStatus( $biblio->{'import_record_id'}, 'imported' ); > # 2nd add authorities if applicable > if (C4::Context->preference("BiblioAddsAuthorities")){ >- my ($countlinked,$countcreated)=BiblioAddAuthorities($marcrecord, $cgiparams->{'frameworkcode'}); >+ my $headings_linked =BiblioAutoLink($marcrecord, $cgiparams->{'frameworkcode'}); > } > } else { > SetImportRecordStatus( $biblio->{'import_record_id'}, 'imported' ); >diff --git a/acqui/neworderempty.pl b/acqui/neworderempty.pl >index 459dfe8..ba856b2 100755 >--- a/acqui/neworderempty.pl >+++ b/acqui/neworderempty.pl >@@ -86,7 +86,7 @@ use C4::Input; > use C4::Koha; > use C4::Branch; # GetBranches > use C4::Members; >-use C4::Search qw/FindDuplicate BiblioAddAuthorities/; >+use C4::Search qw/FindDuplicate/; > > #needed for z3950 import: > use C4::ImportBatch qw/GetImportRecordMarc SetImportRecordStatus/; >@@ -148,7 +148,7 @@ if ( $ordernumber eq '' and defined $params->{'breedingid'}){ > } > #from this point: add a new record > if (C4::Context->preference("BiblioAddsAuthorities")){ >- my ($countlinked,$countcreated)=BiblioAddAuthorities($marcrecord, $params->{'frameworkcode'}); >+ my $headings_linked=BiblioAutoLink($marcrecord, $params->{'frameworkcode'}); > } > my $bibitemnum; > $params->{'frameworkcode'} or $params->{'frameworkcode'} = ""; >diff --git a/authorities/auth_finder.pl b/authorities/auth_finder.pl >index 99ac2c1..932886b 100755 >--- a/authorities/auth_finder.pl >+++ b/authorities/auth_finder.pl >@@ -60,7 +60,7 @@ if ( $op eq "do_search" ) { > my @and_or = $query->param('and_or'); > my @excluding = $query->param('excluding'); > my @operator = $query->param('operator'); >- my @value = ($query->param('value_mainstr')||undef, $query->param('value_main')||undef, $query->param('value_any')||undef); >+ my @value = ($query->param('value_mainstr')||undef, $query->param('value_main')||undef, $query->param('value_any')||undef, $query->param('value_match')||undef); > my $orderby = $query->param('orderby'); > > $resultsperpage = $query->param('resultsperpage'); >@@ -106,6 +106,7 @@ if ( $op eq "do_search" ) { > push @field_data, { term => "value_mainstr", val => $query->param('value_mainstr') || "" }; > push @field_data, { term => "value_main", val => $query->param('value_main') || "" }; > push @field_data, { term => "value_any", val => $query->param('value_any') || ""}; >+ push @field_data, { term => "value_match", val => $query->param('value_match') || ""}; > > my @numbers = (); > >@@ -162,6 +163,7 @@ if ( $op eq "do_search" ) { > value_mainstr => $query->param('value_mainstr') || "", > value_main => $query->param('value_main') || "", > value_any => $query->param('value_any') || "", >+ value_match => $query->param('value_match') || "", > ); > } else { > ( $template, $loggedinuser, $cookie ) = get_template_and_user( >@@ -183,13 +185,11 @@ $template->param( > value_mainstr => $query->param('value_mainstr') || "", > value_main => $query->param('value_main') || "", > value_any => $query->param('value_any') || "", >+ value_match => $query->param('value_match') || "", > tagid => $tagid, > index => $index, > authtypesloop => \@authtypesloop, > authtypecode => $authtypecode, >- value_mainstr => $query->param('value_mainstr') || "", >- value_main => $query->param('value_main') || "", >- value_any => $query->param('value_any') || "", > ); > > # Print the page >diff --git a/cataloguing/addbiblio.pl b/cataloguing/addbiblio.pl >index 568007e..8836812 100755 >--- a/cataloguing/addbiblio.pl >+++ b/cataloguing/addbiblio.pl >@@ -736,95 +736,6 @@ sub build_tabs { > $template->param( BIG_LOOP => \@BIG_LOOP ); > } > >-# >-# sub that tries to find authorities linked to the biblio >-# the sub : >-# - search in the authority DB for the same authid (in $9 of the biblio) >-# - search in the authority DB for the same 001 (in $3 of the biblio in UNIMARC) >-# - search in the authority DB for the same values (exactly) (in all subfields of the biblio) >-# if the authority is found, the biblio is modified accordingly to be connected to the authority. >-# if the authority is not found, it's added, and the biblio is then modified to be connected to the authority. >-# >- >-sub BiblioAddAuthorities{ >- my ( $record, $frameworkcode ) = @_; >- my $dbh=C4::Context->dbh; >- my $query=$dbh->prepare(qq| >-SELECT authtypecode,tagfield >-FROM marc_subfield_structure >-WHERE frameworkcode=? >-AND (authtypecode IS NOT NULL AND authtypecode<>\"\")|); >-# SELECT authtypecode,tagfield >-# FROM marc_subfield_structure >-# WHERE frameworkcode=? >-# AND (authtypecode IS NOT NULL OR authtypecode<>\"\")|); >- $query->execute($frameworkcode); >- my ($countcreated,$countlinked); >- while (my $data=$query->fetchrow_hashref){ >- foreach my $field ($record->field($data->{tagfield})){ >- next if ($field->subfield('3') || $field->subfield('9')); >- # No authorities id in the tag. >- # Search if there is any authorities to link to. >- my $query='at='.$data->{authtypecode}.' '; >- map {$query.= ' and he,ext="'.$_->[1].'"' if ($_->[0]=~/[A-z]/)} $field->subfields(); >- my ($error, $results, $total_hits)=SimpleSearch( $query, undef, undef, [ "authorityserver" ] ); >- # there is only 1 result >- if ( $error ) { >- warn "BIBLIOADDSAUTHORITIES: $error"; >- return (0,0) ; >- } >- if ( @{$results} == 1) { >- my $marcrecord = MARC::File::USMARC::decode($results->[0]); >- $field->add_subfields('9'=>$marcrecord->field('001')->data); >- $countlinked++; >- } elsif (@{$results} > 1) { >- #More than One result >- #This can comes out of a lack of a subfield. >-# my $marcrecord = MARC::File::USMARC::decode($results->[0]); >-# $record->field($data->{tagfield})->add_subfields('9'=>$marcrecord->field('001')->data); >- $countlinked++; >- } else { >- #There are no results, build authority record, add it to Authorities, get authid and add it to 9 >- ###NOTICE : This is only valid if a subfield is linked to one and only one authtypecode >- ###NOTICE : This can be a problem. We should also look into other types and rejected forms. >- my $authtypedata=GetAuthType($data->{authtypecode}); >- next unless $authtypedata; >- my $marcrecordauth=MARC::Record->new(); >- if (C4::Context->preference('marcflavour') eq 'MARC21') { >- $marcrecordauth->leader(' nz a22 o 4500'); >- SetMarcUnicodeFlag($marcrecordauth, 'MARC21'); >- } >- my $authfield=MARC::Field->new($authtypedata->{auth_tag_to_report},'','',"a"=>"".$field->subfield('a')); >- map { $authfield->add_subfields($_->[0]=>$_->[1]) if ($_->[0]=~/[A-z]/ && $_->[0] ne "a" )} $field->subfields(); >- $marcrecordauth->insert_fields_ordered($authfield); >- >- # bug 2317: ensure new authority knows it's using UTF-8; currently >- # only need to do this for MARC21, as MARC::Record->as_xml_record() handles >- # automatically for UNIMARC (by not transcoding) >- # FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record >- # use UTF-8, but as of 2008-08-05, did not want to introduce that kind >- # of change to a core API just before the 3.0 release. >- >- if (C4::Context->preference('marcflavour') eq 'MARC21') { >- $marcrecordauth->insert_fields_ordered(MARC::Field->new('667','','','a'=>"Machine generated authority record.")); >- my $cite = $record->author() . ", " . $record->title_proper() . ", " . $record->publication_date() . " "; >- $cite =~ s/^[\s\,]*//; >- $cite =~ s/[\s\,]*$//; >- $cite = "Work cat.: (" . C4::Context->preference('MARCOrgCode') . ")". $record->subfield('999','c') . ": " . $cite; >- $marcrecordauth->insert_fields_ordered(MARC::Field->new('670','','','a'=>$cite)); >- } >- >-# warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted; >- >- my $authid=AddAuthority($marcrecordauth,'',$data->{authtypecode}); >- $countcreated++; >- $field->add_subfields('9'=>$authid); >- } >- } >- } >- return ($countlinked,$countcreated); >-} >- > # ======================== > # MAIN > #========================= >@@ -957,7 +868,7 @@ if ( $op eq "addbiblio" ) { > my $oldbibnum; > my $oldbibitemnum; > if (C4::Context->preference("BiblioAddsAuthorities")){ >- my ($countlinked,$countcreated)=BiblioAddAuthorities($record,$frameworkcode); >+ my ($countlinked,$countcreated)=BiblioAutoLink($record,$frameworkcode); > } > if ( $is_a_modif ) { > ModBiblioframework( $biblionumber, $frameworkcode ); >diff --git a/etc/zebradb/authorities/etc/bib1.att b/etc/zebradb/authorities/etc/bib1.att >index b34e6b5..ef462f3 100644 >--- a/etc/zebradb/authorities/etc/bib1.att >+++ b/etc/zebradb/authorities/etc/bib1.att >@@ -74,6 +74,7 @@ att 8807 Subdivision-see-also-from > att 8808 Subdivision-see-from > att 8809 Match-heading-see-from > att 8810 Match-subdivision-see-from >+att 8900 Match > > # General Subdivision (defined in att 47) > att 9023 General-subdivision >diff --git a/etc/zebradb/marc_defs/marc21/authorities/authority-koha-indexdefs.xml b/etc/zebradb/marc_defs/marc21/authorities/authority-koha-indexdefs.xml >index 87d31bc..67b234f 100644 >--- a/etc/zebradb/marc_defs/marc21/authorities/authority-koha-indexdefs.xml >+++ b/etc/zebradb/marc_defs/marc21/authorities/authority-koha-indexdefs.xml >@@ -65,6 +65,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="100" subfields="abcdefghjklmnopqrstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -78,6 +80,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="400" subfields="abcdefghjklmnopqrstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -90,6 +94,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="500" subfields="abcdefghjklmnopqrstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > <!-- Corporate name headings --> > <kohaidx:index_subfields tag="110" subfields="abcdefghklmnoprstvxyz"> >@@ -110,6 +118,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="110" subfields="abcdefghklmnoprstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -123,6 +133,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="410" subfields="abcdefghklmnoprstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -135,6 +147,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="510" subfields="abcdefghklmnoprstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > <!-- Meeting name --> > <kohaidx:index_subfields tag="111" subfields="acdefghjklnpqstvxyz"> >@@ -155,6 +171,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="111" subfields="acdefghjklnpqstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -168,6 +186,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="411" subfields="acdefghjklnpqstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -180,6 +200,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="511" subfields="acdefghjklnpqstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > <!-- Uniform title --> > <kohaidx:index_subfields tag="130" subfields="adfghklmnoprstvxyz"> >@@ -200,6 +224,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="130" subfields="adfghklmnoprstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -213,6 +239,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="430" subfields="adfghklmnoprstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -225,6 +253,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="530" subfields="adfghklmnoprstvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > <!-- Chronological term --> > <kohaidx:index_subfields tag="148" subfields="avxyz"> >@@ -245,6 +277,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="148" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -258,6 +292,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="448" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -270,6 +306,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="548" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > > <!-- Topical term --> >@@ -291,6 +331,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="150" subfields="abvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -304,6 +346,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="450" subfields="abvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -316,6 +360,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="550" subfields="abvxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > <!-- Geographic name --> > <kohaidx:index_subfields tag="151" subfields="avxyz"> >@@ -336,6 +384,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="151" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -349,6 +399,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="451" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -361,6 +413,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="551" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > <!-- Genre/form term --> > <kohaidx:index_subfields tag="155" subfields="avxyz"> >@@ -381,6 +437,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>Heading-Main:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="155" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -394,6 +452,8 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-from:s</kohaidx:target_index> > </kohaidx:index_heading> > <kohaidx:index_match_heading tag="455" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:p</kohaidx:target_index> > <kohaidx:target_index>Match-heading-see-from:s</kohaidx:target_index> > </kohaidx:index_match_heading> >@@ -406,6 +466,10 @@ authority-zebra-indexdefs.xsl` > <kohaidx:target_index>See-also-from:p</kohaidx:target_index> > <kohaidx:target_index>See-also-from:s</kohaidx:target_index> > </kohaidx:index_heading> >+ <kohaidx:index_match_heading tag="555" subfields="avxyz" subdivisions="vxyz"> >+ <kohaidx:target_index>Match:w</kohaidx:target_index> >+ <kohaidx:target_index>Match:p</kohaidx:target_index> >+ </kohaidx:index_match_heading> > > <!-- General subdivision --> > <kohaidx:index_heading tag="180" subfields="vxyz" subdivisions="vxyz"> >diff --git a/etc/zebradb/marc_defs/marc21/authorities/authority-zebra-indexdefs.xsl b/etc/zebradb/marc_defs/marc21/authorities/authority-zebra-indexdefs.xsl >index 5c37bc6..3ee5d6c 100644 >--- a/etc/zebradb/marc_defs/marc21/authorities/authority-zebra-indexdefs.xsl >+++ b/etc/zebradb/marc_defs/marc21/authorities/authority-zebra-indexdefs.xsl >@@ -1101,10 +1101,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='100']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('abcdefghjklmnopqrstvxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('abcdefghjklmnopqrstvxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1128,7 +1128,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1136,10 +1140,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='400']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('abcdefghjklmnopqrstvxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('abcdefghjklmnopqrstvxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1163,7 +1167,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='500']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('abcdefghjklmnopqrstvxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1171,10 +1218,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='110']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('abcdefghklmnoprstvxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('abcdefghklmnoprstvxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1198,7 +1245,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1206,10 +1257,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='410']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('abcdefghklmnoprstvxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('abcdefghklmnoprstvxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1233,7 +1284,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='510']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('abcdefghklmnoprstvxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1241,10 +1335,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='111']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('acdefghjklnpqstvxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('acdefghjklnpqstvxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1268,7 +1362,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1276,10 +1374,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='411']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('acdefghjklnpqstvxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('acdefghjklnpqstvxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1303,7 +1401,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='511']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('acdefghjklnpqstvxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1311,10 +1452,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='130']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('adfghklmnoprstvxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('adfghklmnoprstvxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1338,7 +1479,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1346,10 +1491,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='430']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('adfghklmnoprstvxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('adfghklmnoprstvxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1373,7 +1518,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='530']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('adfghklmnoprstvxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1381,10 +1569,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='148']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('avxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1408,7 +1596,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1416,10 +1608,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='448']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('avxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1443,7 +1635,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='548']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1451,10 +1686,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='150']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('abvxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('abvxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1478,7 +1713,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1486,10 +1725,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='450']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('abvxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('abvxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1513,7 +1752,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='550']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('abvxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1521,10 +1803,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='151']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('avxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1548,7 +1830,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1556,10 +1842,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='451']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('avxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1583,7 +1869,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='551']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1591,10 +1920,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='155']"> >- <z:index name="Match-heading:p Match-heading:s"> >+ <z:index name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('avxyz', @code)" name="Match-heading:p Match-heading:s"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p Match-heading:p Match-heading:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1618,7 +1947,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1626,10 +1959,10 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </z:index> > </xslo:template> > <xslo:template mode="index_match_heading" match="marc:datafield[@tag='455']"> >- <z:index name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <z:index name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:variable name="raw_heading"> > <xslo:for-each select="marc:subfield"> >- <xslo:if test="contains('avxyz', @code)" name="Match-heading-see-from:p Match-heading-see-from:s"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p Match-heading-see-from:p Match-heading-see-from:s"> > <xslo:if test="position() > 1"> > <xslo:choose> > <xslo:when test="contains('vxyz', @code)"> >@@ -1653,7 +1986,50 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> >+ </xslo:if> >+ </xslo:for-each> >+ </xslo:variable> >+ <xslo:value-of select="normalize-space($raw_heading)"/> >+ </z:index> >+ </xslo:template> >+ <xslo:template mode="index_match_heading" match="marc:datafield[@tag='555']"> >+ <z:index name="Match:w Match:p"> >+ <xslo:variable name="raw_heading"> >+ <xslo:for-each select="marc:subfield"> >+ <xslo:if test="contains('avxyz', @code)" name="Match:w Match:p"> >+ <xslo:if test="position() > 1"> >+ <xslo:choose> >+ <xslo:when test="contains('vxyz', @code)"> >+ <xslo:choose> >+ <xslo:when test="@code = $general_subdivision_subfield"> >+ <xslo:text> generalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $form_subdivision_subfield"> >+ <xslo:text> formsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $chronological_subdivision_subfield"> >+ <xslo:text> chronologicalsubdiv </xslo:text> >+ </xslo:when> >+ <xslo:when test="@code = $geographic_subdivision_subfield"> >+ <xslo:text> geographicsubdiv </xslo:text> >+ </xslo:when> >+ </xslo:choose> >+ </xslo:when> >+ <xslo:otherwise> >+ <xslo:value-of select="substring(' ', 1, 1)"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ </xslo:if> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1688,7 +2064,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1723,7 +2103,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1758,7 +2142,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1793,7 +2181,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1828,7 +2220,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1863,7 +2259,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1898,7 +2298,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1933,7 +2337,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >@@ -1999,4 +2407,21 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > <xslo:value-of select="."/> > </z:index> > </xslo:template> >+ <xslo:template name="chopPunctuation"> >+ <xslo:param name="chopString"/> >+ <xslo:variable name="length" select="string-length($chopString)"/> >+ <xslo:choose> >+ <xslo:when test="$length=0"/> >+ <xslo:when test="contains('-,.:=;!%/', substring($chopString,$length,1))"> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString" select="substring($chopString,1,$length - 1)"/> >+ </xslo:call-template> >+ </xslo:when> >+ <xslo:when test="not($chopString)"/> >+ <xslo:otherwise> >+ <xslo:value-of select="$chopString"/> >+ </xslo:otherwise> >+ </xslo:choose> >+ <xslo:text/> >+ </xslo:template> > </xslo:stylesheet> >diff --git a/etc/zebradb/marc_defs/marc21/authorities/koha-indexdefs-to-zebra.xsl b/etc/zebradb/marc_defs/marc21/authorities/koha-indexdefs-to-zebra.xsl >index 66bf816..bfb79b4 100644 >--- a/etc/zebradb/marc_defs/marc21/authorities/koha-indexdefs-to-zebra.xsl >+++ b/etc/zebradb/marc_defs/marc21/authorities/koha-indexdefs-to-zebra.xsl >@@ -67,6 +67,21 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > <xslo:value-of select="."/> > </z:index> > </xslo:template> >+ <xslo:template name="chopPunctuation"> >+ <xslo:param name="chopString"/> >+ <xslo:variable name="length" select="string-length($chopString)"/> >+ <xslo:choose> >+ <xslo:when test="$length=0"/> >+ <xslo:when test="contains('-,.:=;!%/', substring($chopString,$length,1))"> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString" select="substring($chopString,1,$length - 1)"/> >+ </xslo:call-template> >+ </xslo:when> >+ <xslo:when test="not($chopString)"/> >+ <xslo:otherwise><xslo:value-of select="$chopString"/></xslo:otherwise> >+ </xslo:choose> >+ <xslo:text> </xslo:text> >+ </xslo:template> > </xslo:stylesheet> > </xsl:template> > >@@ -368,7 +383,11 @@ authority-zebra-indexdefs.xsl` (substituting the appropriate file names). > </xslo:otherwise> > </xslo:choose> > </xslo:if> >- <xslo:value-of select="."/> >+ <xslo:call-template name="chopPunctuation"> >+ <xslo:with-param name="chopString"> >+ <xslo:value-of select="."/> >+ </xslo:with-param> >+ </xslo:call-template> > </xslo:if> > </xslo:for-each> > </xslo:variable> >diff --git a/etc/zebradb/marc_defs/unimarc/authorities/record.abs b/etc/zebradb/marc_defs/unimarc/authorities/record.abs >index 23ac408..0467c5e 100644 >--- a/etc/zebradb/marc_defs/unimarc/authorities/record.abs >+++ b/etc/zebradb/marc_defs/unimarc/authorities/record.abs >@@ -16,89 +16,89 @@ melm 942$a authtype > melm 152$b authtype:w,authtype:p > > # Personal Name >-melm 200$a Personal-name-heading:w,Personal-name-heading:p,Personal-name-heading:s,Personal-name:w,Personal-name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 200 Personal-name:w,Personal-name:p,Heading:w,Heading:p >-melm 400 Personal-name-see:w,Personal-name-see:p,See:w,See:p >-melm 500 Personal-name-see-also:w,Personal-name-see-also:p,See-also:w,See-also:p >-melm 700$a Personal-name-parallel:w,Personal-name-parallel:p,Parallel:w,Parallel:p >+melm 200$a Personal-name-heading:w,Personal-name-heading:p,Personal-name-heading:s,Personal-name:w,Personal-name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 200 Personal-name:w,Personal-name:p,Heading:w,Heading:p,Match:w,Match:p >+melm 400 Personal-name-see:w,Personal-name-see:p,See:w,See:p,Match:w,Match:p >+melm 500 Personal-name-see-also:w,Personal-name-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 700$a Personal-name-parallel:w,Personal-name-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Corporate Name >-melm 210$a Corporate-name-heading:w,Corporate-name-heading:p,Corporate-name-heading:s,Corporate-name:w,Corporate-name:p,Conference-name-heading:w,Conference-name-heading:p,Conference-name-heading:s,Conference-name:w,Conference-name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 210 Corporate-name:w,Corporate-name:p,Conference-name:w,Conference-name:p,Heading:w,Heading:p >-melm 410 Corporate-name-see:w,Corporate-name-see:p,Conference-name-see:w,Conference-name-see:p,See:w,See:p >-melm 510 Corporate-name-see-also:w,Corporate-name-see-also:p,Conference-name-see-also:w,Conference-name-see-also:p,See-also:w,See-also:p >-melm 710 Corporate-name-parallel:w,Corporate-name-parallel:p,Parallel:w,Parallel:p >+melm 210$a Corporate-name-heading:w,Corporate-name-heading:p,Corporate-name-heading:s,Corporate-name:w,Corporate-name:p,Conference-name-heading:w,Conference-name-heading:p,Conference-name-heading:s,Conference-name:w,Conference-name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 210 Corporate-name:w,Corporate-name:p,Conference-name:w,Conference-name:p,Heading:w,Heading:p,Match:w,Match:p >+melm 410 Corporate-name-see:w,Corporate-name-see:p,Conference-name-see:w,Conference-name-see:p,See:w,See:p,Match:w,Match:p >+melm 510 Corporate-name-see-also:w,Corporate-name-see-also:p,Conference-name-see-also:w,Conference-name-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 710 Corporate-name-parallel:w,Corporate-name-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > > # Geographic & Geographic Name >-melm 215$a Name-geographic-heading:w,Name-geographic-heading:w,Name-geographic-heading:s,Name-geographic:w,Name-geographic:p,Term-geographic-heading:w,Term-geographic-heading:p,Term-geographic-heading:s,Term-geographic:w,Term-geographic:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 215 Name-geographic-heading:w,Name-geographic-heading:w,Name-geographic-heading:s,Name-geographic:w,Name-geographic:p,Term-geographic:w,Term-geographic:p,Term-geographic:s,Heading:w,Heading:p,Heading:s >-melm 415 Name-geographic-see:w,Name-geographic-see:p,Term-geographic-see:w,Term-geographic-see:p,Term-geographic-see:s,See:w,See:p,See:s >-melm 515 Name-geographic-see-also:w,Name-geographic-see-also:p,Term-geographic-see-also:w,Term-geographic-see-also:p,Term-geographic-see-also:s,See-also:w,See-also:p,See-also:s >-melm 715 Name-geographic-parallel:w,Name-geographic-parallel:p,Term-geographic-parallel:w,Term-geographic-parallel:p,Term-geographic-parallel:s,Parallel:w,Parallel:p,Parallel:s >+melm 215$a Name-geographic-heading:w,Name-geographic-heading:w,Name-geographic-heading:s,Name-geographic:w,Name-geographic:p,Term-geographic-heading:w,Term-geographic-heading:p,Term-geographic-heading:s,Term-geographic:w,Term-geographic:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 215 Name-geographic-heading:w,Name-geographic-heading:w,Name-geographic-heading:s,Name-geographic:w,Name-geographic:p,Term-geographic:w,Term-geographic:p,Term-geographic:s,Heading:w,Heading:p,Heading:s,Match:w,Match:p >+melm 415 Name-geographic-see:w,Name-geographic-see:p,Term-geographic-see:w,Term-geographic-see:p,Term-geographic-see:s,See:w,See:p,See:s,Match:w,Match:p >+melm 515 Name-geographic-see-also:w,Name-geographic-see-also:p,Term-geographic-see-also:w,Term-geographic-see-also:p,Term-geographic-see-also:s,See-also:w,See-also:p,See-also:s,Match:w,Match:p >+melm 715 Name-geographic-parallel:w,Name-geographic-parallel:p,Term-geographic-parallel:w,Term-geographic-parallel:p,Term-geographic-parallel:s,Parallel:w,Parallel:p,Parallel:s,Match:w,Match:p > > # Trademark >-melm 216$a Trademark-heading:w,Trademark-heading:p,Trademark-heading:s,Trademark:w,Trademark:p,Conference-name-heading:w,Conference-name-heading:p,Conference-name-heading:s,Conference-name:w,Conference-name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 216 Trademark:w,Trademark:p,Conference-name:w,Conference-name:p,Heading:w,Heading:p >-melm 416 Trademark-see:w,Trademark-see:p,Conference-name-see:w,Conference-name-see:p,See:w,See:p >-melm 516 Trademark-see-also:w,Trademark-see-also:p,Conference-name-see-also:w,Conference-name-see-also:p,See-also:w,See-also:p >-melm 716 Trademark-parallel:w,Trademark-parallel:p,Parallel:w,Parallel:p >+melm 216$a Trademark-heading:w,Trademark-heading:p,Trademark-heading:s,Trademark:w,Trademark:p,Conference-name-heading:w,Conference-name-heading:p,Conference-name-heading:s,Conference-name:w,Conference-name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 216 Trademark:w,Trademark:p,Conference-name:w,Conference-name:p,Heading:w,Heading:p,Match:w,Match:p >+melm 416 Trademark-see:w,Trademark-see:p,Conference-name-see:w,Conference-name-see:p,See:w,See:p,Match:w,Match:p >+melm 516 Trademark-see-also:w,Trademark-see-also:p,Conference-name-see-also:w,Conference-name-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 716 Trademark-parallel:w,Trademark-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Family Name >-melm 220$a Name-heading:w,Name-heading:p,Name-heading:s,Name:w,Name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 220 Name:w,Name:p,Name:s,Heading:w,Heading:p,Heading:s >-melm 420 Name-see:w,Name-see:p,Name-see:s,See:w,See:p,See:s >-melm 520 Name-see-also:w,Name-see-also:p,Name-see-also:s,See-also:w,See-also:p,See-also:s >-melm 720 Name-parallel:w,Name-parallel:p,Name-parallel:s,Parallel:w,Parallel:p,Parallel:s >+melm 220$a Name-heading:w,Name-heading:p,Name-heading:s,Name:w,Name:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 220 Name:w,Name:p,Name:s,Heading:w,Heading:p,Heading:s,Match:w,Match:p >+melm 420 Name-see:w,Name-see:p,Name-see:s,See:w,See:p,See:s,Match:w,Match:p >+melm 520 Name-see-also:w,Name-see-also:p,Name-see-also:s,See-also:w,See-also:p,See-also:s,Match:w,Match:p >+melm 720 Name-parallel:w,Name-parallel:p,Name-parallel:s,Parallel:w,Parallel:p,Parallel:s,Match:w,Match:p > > # Uniform Title >-melm 230$a Title-uniform-heading:w,Title-uniform-heading:p,Title-uniform-heading:s,Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 230 Title-uniform:w,Title-uniform:p,Heading:w,Heading:p >-melm 430 Title-uniform-see:w,Title-uniform-see:p,See:w,See:p >-melm 530 Title-uniform-see-also:w,Title-uniform-see-also:p,See-also:w,See-also:p >-melm 730$a Title-uniform-parallel:w,Title-uniform-parallel:p,Parallel:w,Parallel:p >+melm 230$a Title-uniform-heading:w,Title-uniform-heading:p,Title-uniform-heading:s,Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 230 Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Match:w,Match:p >+melm 430 Title-uniform-see:w,Title-uniform-see:p,See:w,See:p,Match:w,Match:p >+melm 530 Title-uniform-see-also:w,Title-uniform-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 730$a Title-uniform-parallel:w,Title-uniform-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Second area for uniform titles in UNIMARC? >-melm 235$a Title-uniform-heading:w,Title-uniform-heading:p,Title-uniform-heading:s,Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 235 Title-uniform:w,Title-uniform:p,Heading:w,Heading:p >-melm 435 Title-uniform-see:w,Title-uniform-see:p,See:w,See:p >-melm 535 Title-uniform-see-also:w,Title-uniform-see-also:p,See-also:w,See-also:p >-melm 735$a Title-uniform-parallel:w,Title-uniform-parallel:p,Parallel:w,Parallel:p >+melm 235$a Title-uniform-heading:w,Title-uniform-heading:p,Title-uniform-heading:s,Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 235 Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Match:w,Match:p >+melm 435 Title-uniform-see:w,Title-uniform-see:p,See:w,See:p,Match:w,Match:p >+melm 535 Title-uniform-see-also:w,Title-uniform-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 735$a Title-uniform-parallel:w,Title-uniform-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Uniform Title >-melm 240$a Name-Title-heading:w,Name-Title-heading:p,Name-Title-heading:s,Name-Title:w,Name-Title:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 240 Name-Title:w,Name-Title:p,Heading:w,Heading:p >-melm 440 Name-Title-see:w,Name-Title-see:p,See:w,See:p >-melm 540 Name-Title-see-also:w,Name-Title-see-also:p,See-also:w,See-also:p >-melm 740$a Name-Title-parallel:w,Name-Title-parallel:p,Parallel:w,Parallel:p >+melm 240$a Name-Title-heading:w,Name-Title-heading:p,Name-Title-heading:s,Name-Title:w,Name-Title:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 240 Name-Title:w,Name-Title:p,Heading:w,Heading:p,Match:w,Match:p >+melm 440 Name-Title-see:w,Name-Title-see:p,See:w,See:p,Match:w,Match:p >+melm 540 Name-Title-see-also:w,Name-Title-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 740$a Name-Title-parallel:w,Name-Title-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Second area for uniform titles in UNIMARC? >-melm 245$a Title-uniform-heading:w,Title-uniform-heading:p,Title-uniform-heading:s,Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 245 Title-uniform:w,Title-uniform:p,Heading:w,Heading:p >-melm 445 Title-uniform-see:w,Title-uniform-see:p,See:w,See:p >-melm 545 Title-uniform-see-also:w,Title-uniform-see-also:p,See-also:w,See-also:p >-melm 745$a Title-uniform-parallel:w,Title-uniform-parallel:p,Parallel:w,Parallel:p >+melm 245$a Title-uniform-heading:w,Title-uniform-heading:p,Title-uniform-heading:s,Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 245 Title-uniform:w,Title-uniform:p,Heading:w,Heading:p,Match:w,Match:p >+melm 445 Title-uniform-see:w,Title-uniform-see:p,See:w,See:p,Match:w,Match:p >+melm 545 Title-uniform-see-also:w,Title-uniform-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 745$a Title-uniform-parallel:w,Title-uniform-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Topical Term >-melm 250$a Subject-heading:w,Subject-heading:p,Subject-heading:s,Subject:w,Subject:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 250 Subject:w,Subject:p,Heading:w,Heading:p,Subject-heading:w,Subject-heading:p,Subject-heading:s >-melm 450 Subject-see:w,Subject-see:p,See:w,See:p >-melm 550 Subject-see-also:w,Subject-see-also:p,See-also:w,See-also:p >-melm 750$a Subject-parallel:w,Subject-parallel:p,Parallel:w,Parallel:p >+melm 250$a Subject-heading:w,Subject-heading:p,Subject-heading:s,Subject:w,Subject:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 250 Subject:w,Subject:p,Heading:w,Heading:p,Subject-heading:w,Subject-heading:p,Subject-heading:s,Match:w,Match:p >+melm 450 Subject-see:w,Subject-see:p,See:w,See:p,Match:w,Match:p >+melm 550 Subject-see-also:w,Subject-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 750$a Subject-parallel:w,Subject-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Place Access >-melm 260$a Place-heading:w,Place-heading:p,Place-heading:s,Place:w,Place:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 260 Place:w,Place:p,Heading:w,Heading:p,Place-heading:w,Place-heading:p,Place-heading:s >-melm 460 Place-see:w,Place-see:p,See:w,See:p >-melm 560 Place-see-also:w,Place-see-also:p,See-also:w,See-also:p >-melm 760$a Place-parallel:w,Place-parallel:p,Parallel:w,Parallel:p >+melm 260$a Place-heading:w,Place-heading:p,Place-heading:s,Place:w,Place:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 260 Place:w,Place:p,Heading:w,Heading:p,Place-heading:w,Place-heading:p,Place-heading:s,Match:w,Match:p >+melm 460 Place-see:w,Place-see:p,See:w,See:p,Match:w,Match:p >+melm 560 Place-see-also:w,Place-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 760$a Place-parallel:w,Place-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > # Form Genre >-melm 280$a Form-heading:w,Form-heading:p,Form-heading:s,Form:w,Form:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s >-melm 280 Form:w,Form:p,Heading:w,Heading:p,Form-heading:w,Form-heading:p,Form-heading:s >-melm 480 Form-see:w,Form-see:p,See:w,See:p >-melm 580 Form-see-also:w,Form-see-also:p,See-also:w,See-also:p >-melm 780$a Form-parallel:w,Form-parallel:p,Parallel:w,Parallel:p >+melm 280$a Form-heading:w,Form-heading:p,Form-heading:s,Form:w,Form:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p >+melm 280 Form:w,Form:p,Heading:w,Heading:p,Form-heading:w,Form-heading:p,Form-heading:s,Match:w,Match:p >+melm 480 Form-see:w,Form-see:p,See:w,See:p,Match:w,Match:p >+melm 580 Form-see-also:w,Form-see-also:p,See-also:w,See-also:p,Match:w,Match:p >+melm 780$a Form-parallel:w,Form-parallel:p,Parallel:w,Parallel:p,Match:w,Match:p > > > # NOTE: subdivisions management missing from Koha >diff --git a/installer/data/mysql/atomicupdate/bug_7284_authority_linking_pt1 b/installer/data/mysql/atomicupdate/bug_7284_authority_linking_pt1 >new file mode 100644 >index 0000000..c8a1f42 >--- /dev/null >+++ b/installer/data/mysql/atomicupdate/bug_7284_authority_linking_pt1 >@@ -0,0 +1,21 @@ >+#! /usr/bin/perl >+use strict; >+use warnings; >+use C4::Context; >+my $dbh = C4::Context->dbh; >+$dbh->do( >+"INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerModule','Default','Chooses which linker module to use (see documentation).','Default|FirstMatchLastMatch','Choice');" >+); >+$dbh->do( >+"INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerOptions','','A pipe-separated list of options for the linker.','','free');" >+); >+$dbh->do( >+"INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerRelink',1,'If ON the authority linker will relink headings that have previously been linked every time it runs.',NULL,'YesNo');" >+); >+$dbh->do( >+"INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerKeepStale',0,'If ON the authority linker will keep existing authority links for headings where it is unable to find a match.',NULL,'YesNo');" >+); >+$dbh->do( >+"INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('AutoCreateAuthorities',0,'Automatically create authorities that do not exist when cataloging records.',NULL,'YesNo');" >+); >+print "Upgrade done (Configured bug 7284, improved authority matching)\n"; >diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql >index 2a59b47..66083e6 100755 >--- a/installer/data/mysql/sysprefs.sql >+++ b/installer/data/mysql/sysprefs.sql >@@ -333,3 +333,8 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('OPACLocalCoverImages','0','Display local cover images on OPAC search and details pages.','1','YesNo'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('LocalCoverImages','0','Display local cover images on intranet details pages.','1','YesNo'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('AllowMultipleCovers','0','Allow multiple cover images to be attached to each bibliographic record.','1','YesNo'); >+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('AutoCreateAuthorities',0,'Automatically create authorities that do not exist when cataloging records.',NULL,'YesNo'); >+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerModule','Default','Chooses which linker module to use (see documentation).','Default|FirstMatch|LastMatch','Choice'); >+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerOptions','','A pipe-separated list of options for the linker.','','free'); >+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerRelink',1,'If ON the authority linker will relink headings that have previously been linked every time it runs.',NULL,'YesNo'); >+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('LinkerKeepStale',0,'If ON the authority linker will keep existing authority links for headings where it is unable to find a match.',NULL,'YesNo'); >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/auth-finder-search.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/auth-finder-search.inc >index 895658a..3c7fd41 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/auth-finder-search.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/auth-finder-search.inc >@@ -33,7 +33,7 @@ > </li> > <li> > <label for="marclist">Anywhere</label> >- <input type="hidden" name="marclist" value="" /> >+ <input type="hidden" name="marclist" value="any" /> > <input type="hidden" name="and_or" value="and" /> > <input type="hidden" name="excluding" value="" /> > <select name="operator" id="marclist"> >@@ -43,6 +43,18 @@ > </select> > <input type="text" name="value_any" value="[% value_any |html %]" /> > </li> >+ <li> >+ <label for="marclist">Heading match</label> >+ <input type="hidden" name="marclist" value="match" /> >+ <input type="hidden" name="and_or" value="and" /> >+ <input type="hidden" name="excluding" value="" /> >+ <select name="operator" id="marclist"> >+ <option value="contains">contains</option> >+ <option value="start">starts with</option> >+ <option value="is">is exactly</option> >+ </select> >+ <input type="text" name="value_match" value="[% value_match |html %]" /> >+ </li> > <li> > <label for="orderby">Sort by </label> > <select name="orderby" id="orderby"> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/authorities.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/authorities.pref >index e61f622..953c97d 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/authorities.pref >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/authorities.pref >@@ -1,29 +1,67 @@ > Authorities: >- - >- - When editing records, >- - pref: BiblioAddsAuthorities >- default: no >- choices: >- yes: allow >- no: "don't allow" >- - them to automatically create new authority records if needed, rather than having to reference existing authorities. >- - >- - pref: dontmerge >- default: yes >- choices: >- yes: "Don't" >- no: Do >- - automatically update attached biblios when changing an authority record. If this is off, please ask your administrator to enable the merge_authority.pl cronjob. >- - >- - Use the following text for the contents of MARC authority control field 008 position 06-39 (fixed length data elements). Do NOT include the date (position 00-05). >- - pref: MARCAuthorityControlField008 >- default: "|| aca||aabn | a|a d" >- type: textarea >- class: code >- - >- - pref: UseAuthoritiesForTracings >- default: yes >- choices: >- yes: Use >- no: "Don't use" >- - authority record numbers instead of text strings for searches from subject tracings. >+ General: >+ - >+ - When editing records, >+ - pref: BiblioAddsAuthorities >+ default: no >+ choices: >+ yes: allow >+ no: "don't allow" >+ - them to automatically create new authority records if needed, rather than having to reference existing authorities. >+ - >+ - When editing records, >+ - pref: AutoCreateAuthorities >+ default: yes >+ choices: >+ yes: generate >+ no: "do not generate" >+ - authority records that are missing (BiblioAddsAuthorities must be set to "allow" for this to have any effect). >+ - >+ - pref: dontmerge >+ default: yes >+ choices: >+ yes: "Don't" >+ no: Do >+ - automatically update attached biblios when changing an authority record. If this is off, please ask your administrator to enable the merge_authority.pl cronjob. >+ - >+ - Use the following text for the contents of MARC authority control field 008 position 06-39 (fixed length data elements). Do NOT include the date (position 00-05). >+ - pref: MARCAuthorityControlField008 >+ default: "|| aca||aabn | a|a d" >+ type: textarea >+ class: code >+ - >+ - pref: UseAuthoritiesForTracings >+ default: yes >+ choices: >+ yes: Use >+ no: "Don't use" >+ - authority record numbers instead of text strings for searches from subject tracings. >+ Linker: >+ - >+ - Use the >+ - pref: LinkerModule >+ default: Default >+ choices: >+ Default: Default >+ FirstMatch: "First Match" >+ LastMatch: "Last Match" >+ - linker module for matching headings to authority records. >+ - >+ - Set the following options for the authority linker >+ - pref: LinkerOptions >+ class: multi >+ - (separate options with |) >+ - >+ - pref: LinkerRelink >+ default: yes >+ choices: >+ yes: Do >+ no: "Do not" >+ - relink headings that have previously been linked to authority records. >+ - >+ - pref: LinkerKeepStale >+ default: no >+ choices: >+ yes: Do >+ no: "Do not" >+ - keep existing links to authority records for headings where the linker is unable to find a match. >diff --git a/misc/link_bibs_to_authorities.pl b/misc/link_bibs_to_authorities.pl >index 0fc9c26..b35a00e 100755 >--- a/misc/link_bibs_to_authorities.pl >+++ b/misc/link_bibs_to_authorities.pl >@@ -2,7 +2,9 @@ > > use strict; > use warnings; >+ > BEGIN { >+ > # find Koha's Perl modules > # test carefully before changing this > use FindBin; >@@ -12,86 +14,209 @@ BEGIN { > use C4::Context; > use C4::Biblio; > use Getopt::Long; >+use Pod::Usage; >+use Data::Dumper; >+use Time::HiRes qw/time/; >+use POSIX qw/strftime ceil/; >+ >+sub usage { >+ pod2usage( -verbose => 2 ); >+ exit; >+} > > $| = 1; > > # command-line parameters >-my $verbose = 0; >-my $test_only = 0; >-my $want_help = 0; >+my $verbose = 0; >+my $link_report = 0; >+my $test_only = 0; >+my $want_help = 0; >+my $auth_limit; >+my $bib_limit; >+my $commit = 100; > > my $result = GetOptions( >- 'verbose' => \$verbose, >- 'test' => \$test_only, >- 'h|help' => \$want_help >+ 'v|verbose' => \$verbose, >+ 't|test' => \$test_only, >+ 'l|link-report' => \$link_report, >+ 'a|auth-limit=s' => \$auth_limit, >+ 'b|bib-limit=s' => \$bib_limit, >+ 'c|commit=i' => \$commit, >+ 'h|help' => \$want_help > ); > >-if (not $result or $want_help) { >- print_usage(); >- exit 0; >+binmode( STDOUT, ":utf8" ); >+ >+if ( not $result or $want_help ) { >+ usage(); >+} >+ >+my $linker_module = >+ "C4::Linker::" . ( C4::Context->preference("LinkerModule") || 'Default' ); >+eval { eval "require $linker_module"; }; >+if ($@) { >+ $linker_module = 'C4::Linker::Default'; >+ eval "require $linker_module"; > } >+if ($@) { >+ die "Unable to load linker module. Aborting."; >+} >+ >+my $linker = $linker_module->new( >+ { >+ 'auth_limit' => $auth_limit, >+ 'options' => C4::Context->preference("LinkerOptions") >+ } >+); > > my $num_bibs_processed = 0; >-my $num_bibs_modified = 0; >-my $num_bad_bibs = 0; >+my $num_bibs_modified = 0; >+my $num_bad_bibs = 0; >+my %unlinked_headings; >+my %linked_headings; >+my %fuzzy_headings; > my $dbh = C4::Context->dbh; > $dbh->{AutoCommit} = 0; >-process_bibs(); >+process_bibs( $linker, $bib_limit, $auth_limit, $commit ); > $dbh->commit(); > > exit 0; > > sub process_bibs { >- my $sql = "SELECT biblionumber FROM biblio ORDER BY biblionumber ASC"; >+ my ( $linker, $bib_limit, $auth_limit, $commit ) = @_; >+ my $bib_where = ''; >+ my $starttime = time(); >+ if ($bib_limit) { >+ $bib_where = "WHERE $bib_limit"; >+ } >+ my $sql = >+ "SELECT biblionumber FROM biblio $bib_where ORDER BY biblionumber ASC"; > my $sth = $dbh->prepare($sql); > $sth->execute(); >- while (my ($biblionumber) = $sth->fetchrow_array()) { >+ while ( my ($biblionumber) = $sth->fetchrow_array() ) { > $num_bibs_processed++; >- process_bib($biblionumber); >+ process_bib( $linker, $biblionumber ); > >- if (not $test_only and ($num_bibs_processed % 100) == 0) { >+ if ( not $test_only and ( $num_bibs_processed % $commit ) == 0 ) { > print_progress_and_commit($num_bibs_processed); > } > } > >- if (not $test_only) { >+ if ( not $test_only ) { > $dbh->commit; > } > >- print <<_SUMMARY_; >+ my $headings_linked = 0; >+ my $headings_unlinked = 0; >+ my $headings_fuzzy = 0; >+ for ( values %linked_headings ) { $headings_linked += $_; } >+ for ( values %unlinked_headings ) { $headings_unlinked += $_; } >+ for ( values %fuzzy_headings ) { $headings_fuzzy += $_; } >+ >+ my $endtime = time(); >+ my $totaltime = ceil (($endtime - $starttime) * 1000); >+ $starttime = strftime('%D %T', localtime($starttime)); >+ $endtime = strftime('%D %T', localtime($endtime)); >+ >+ my $summary = <<_SUMMARY_; > > Bib authority heading linking report >------------------------------------- >-Number of bibs checked: $num_bibs_processed >-Number of bibs modified: $num_bibs_modified >-Number of bibs with errors: $num_bad_bibs >+======================================================= >+Linker module: $linker_module >+Run started at: $starttime >+Run ended at: $endtime >+Total run time: $totaltime ms >+Number of bibs checked: $num_bibs_processed >+Number of bibs modified: $num_bibs_modified >+Number of bibs with errors: $num_bad_bibs >+Number of headings linked: $headings_linked >+Number of headings unlinked: $headings_unlinked >+Number of headings fuzzily linked: $headings_fuzzy > _SUMMARY_ >+ $summary .= "\n**** Ran in test mode only ****\n" if $test_only; >+ print $summary; >+ >+ if ($link_report) { >+ my @keys; >+ print <<_LINKED_HEADER_; >+ >+Linked headings (from most frequent to least): >+------------------------------------------------------- >+ >+_LINKED_HEADER_ >+ >+ @keys = sort { >+ $linked_headings{$b} <=> $linked_headings{$a} or "\L$a" cmp "\L$b" >+ } keys %linked_headings; >+ foreach my $key (@keys) { >+ print "$key:\t" . $linked_headings{$key} . " occurrences\n"; >+ } >+ >+ print <<_UNLINKED_HEADER_; >+ >+Unlinked headings (from most frequent to least): >+------------------------------------------------------- >+ >+_UNLINKED_HEADER_ >+ >+ @keys = sort { >+ $unlinked_headings{$b} <=> $unlinked_headings{$a} >+ or "\L$a" cmp "\L$b" >+ } keys %unlinked_headings; >+ foreach my $key (@keys) { >+ print "$key:\t" . $unlinked_headings{$key} . " occurrences\n"; >+ } >+ >+ print <<_FUZZY_HEADER_; >+ >+Fuzzily-matched headings (from most frequent to least): >+------------------------------------------------------- >+ >+_FUZZY_HEADER_ >+ >+ @keys = sort { >+ $fuzzy_headings{$b} <=> $fuzzy_headings{$a} or "\L$a" cmp "\L$b" >+ } keys %fuzzy_headings; >+ foreach my $key (@keys) { >+ print "$key:\t" . $fuzzy_headings{$key} . " occurrences\n"; >+ } >+ print $summary; >+ } > } > > sub process_bib { >+ my $linker = shift; > my $biblionumber = shift; > > my $bib = GetMarcBiblio($biblionumber); >- unless (defined $bib) { >- print "\nCould not retrieve bib $biblionumber from the database - record is corrupt.\n"; >+ unless ( defined $bib ) { >+ print >+"\nCould not retrieve bib $biblionumber from the database - record is corrupt.\n"; > $num_bad_bibs++; > return; > } > >- my $headings_changed = LinkBibHeadingsToAuthorities($bib); >+ my ( $headings_changed, $results ) = >+ LinkBibHeadingsToAuthorities( $linker, $bib, >+ GetFrameworkCode($biblionumber) ); >+ foreach my $key ( keys %{ $results->{'unlinked'} } ) { >+ $unlinked_headings{$key} += $results->{'unlinked'}->{$key}; >+ } >+ foreach my $key ( keys %{ $results->{'linked'} } ) { >+ $linked_headings{$key} += $results->{'linked'}->{$key}; >+ } >+ foreach my $key ( keys %{ $results->{'fuzzy'} } ) { >+ $fuzzy_headings{$key} += $results->{'fuzzy'}->{$key}; >+ } > >- if ($headings_changed) { >+ if ($headings_changed) { > if ($verbose) { >- my $title = substr($bib->title, 0, 20); >- print "Bib $biblionumber ($title): $headings_changed headings changed\n"; >+ my $title = substr( $bib->title, 0, 20 ); >+ print >+"Bib $biblionumber ($title): $headings_changed headings changed\n"; > } >- if (not $test_only) { >- # delete any item tags >- my ($itemtag, $itemsubfield) = GetMarcFromKohaField("items.itemnumber", ''); >- foreach my $field ($bib->field($itemtag)) { >- $bib->delete_field($field); >- } >- ModBiblio($bib, $biblionumber, GetFrameworkCode($biblionumber)); >+ if ( not $test_only ) { >+ ModBiblio( $bib, $biblionumber, GetFrameworkCode($biblionumber) ); > $num_bibs_modified++; > } > } >@@ -103,20 +228,58 @@ sub print_progress_and_commit { > print "... processed $recs records\n"; > } > >-sub print_usage { >- print <<_USAGE_; >-$0: link headings in bib records to authorities. >- >-This batch job checks each bib record in the Koha >-database and attempts to link each of its headings >-to the matching authority record. >- >-Parameters: >- --verbose print the number of headings changed >- for each bib >- --test only test the authority linking >- and report the results; do not >- change the bib records. >- --help or -h show this message. >-_USAGE_ >-} >+=head1 NAME >+ >+link_bibs_to_authorities.pl >+ >+=head1 SYNOPSIS >+ >+ link_bibs_to_authorities.pl >+ link_bibs_to_authorities.pl -v >+ link_bibs_to_authorities.pl -l >+ link_bibs_to_authorities.pl --commit=1000 >+ link_bibs_to_authorities.pl --auth-limit=STRING >+ link_bibs_to_authorities.pl --bib-limit=STRING >+ >+=head1 DESCRIPTION >+ >+This batch job checks each bib record in the Koha database and attempts to link >+each of its headings to the matching authority record. >+ >+=over 8 >+ >+=item B<--help> >+ >+Prints this help >+ >+=item B<-v|--verbose> >+ >+Provide verbose log information (print the number of headings changed for each >+bib record). >+ >+=item B<-l|--link-report> >+ >+Provide a report of all the headings that were processed: which were matched, >+which were not, etc. >+ >+=item B<--auth-limit=S> >+ >+Only process those headings which match an authority record that matches the >+user-specified WHERE clause. >+ >+=item B<--bib-limit=S> >+ >+Only process those bib records that match the user-specified WHERE clause. >+ >+=item B<--commit=N> >+ >+Commit the results to the database after every N records are processed. >+ >+=item B<--test> >+ >+Only test the authority linking and report the results; do not change the bib >+records. >+ >+=back >+ >+=cut >diff --git a/t/Heading_MARC21.t b/t/Heading_MARC21.t >index a0ca21b..198eaea 100755 >--- a/t/Heading_MARC21.t >+++ b/t/Heading_MARC21.t >@@ -6,9 +6,13 @@ > use strict; > use warnings; > >-use Test::More tests => 1; >+use Test::More tests => 3; > > BEGIN { >- use_ok('C4::External::Amazon'); >+ use_ok('C4::Heading'); > } > >+my $field = MARC::Field->new( '650', ' ', '0', a => 'Uncles', x => 'Fiction' ); >+my $heading = C4::Heading->new_from_bib_field($field); >+is($heading->display_form(), 'Uncles--Fiction', 'Display form generation'); >+is($heading->search_form(), 'Uncles generalsubdiv Fiction', 'Search form generation'); >diff --git a/t/db_dependent/Linker_FirstMatch.t b/t/db_dependent/Linker_FirstMatch.t >new file mode 100755 >index 0000000..a2d3c60 >--- /dev/null >+++ b/t/db_dependent/Linker_FirstMatch.t >@@ -0,0 +1,46 @@ >+#!/usr/bin/perl >+# >+# This Koha test module is a stub! >+# Add more tests here!!! >+ >+use strict; >+use warnings; >+use Test::More tests => 3; >+use C4::Context; >+use C4::Heading; >+use MARC::Record; >+use MARC::Field; >+use C4::Linker::FirstMatch; >+ >+ >+BEGIN { >+ use_ok('C4::Linker'); >+} >+my $dbh = C4::Context->dbh; >+ >+my $query = "SELECT authid, marc FROM auth_header LIMIT 1;"; >+my $sth = $dbh->prepare($query); >+$sth->execute(); >+my ($authid, $marc) = $sth->fetchrow_array(); >+SKIP: { >+ skip "No authorities", 2 unless defined $authid; >+ my $linker = C4::Linker::FirstMatch->new(); >+ my $auth = MARC::Record->new_from_usmarc($marc); >+ my $fieldmatch; >+ if (C4::Context->preference('MARCFlavour') eq 'UNIMARC') { >+ $fieldmatch = '2..'; >+ } else { >+ $fieldmatch = '1..'; >+ } >+ my $bibfield = $auth->field($fieldmatch); >+ my $tag = $bibfield->tag(); >+ $tag =~ s/^./6/; >+ $bibfield->update(tag => $tag); >+ my $heading; >+ ok(defined ($heading = C4::Heading->new_from_bib_field($bibfield, '')), "Creating heading from bib field"); >+ >+ my $authmatch; >+ my $fuzzy; >+ ($authmatch, $fuzzy) = $linker->get_link($heading); >+ is($authmatch, $authid, "Matched existing heading"); >+} >diff --git a/t/db_dependent/lib/KohaTest/AuthoritiesMarc.pm b/t/db_dependent/lib/KohaTest/AuthoritiesMarc.pm >index 6114843..4938ccf 100644 >--- a/t/db_dependent/lib/KohaTest/AuthoritiesMarc.pm >+++ b/t/db_dependent/lib/KohaTest/AuthoritiesMarc.pm >@@ -24,7 +24,6 @@ sub methods : Test( 1 ) { > GetAuthorityXML > GetAuthority > GetAuthType >- AUTHhtml2marc > FindDuplicateAuthority > BuildSummary > BuildUnimarcHierarchies >-- >1.7.2.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 7284
:
7083
|
7084
|
7085
|
7086
|
7087
|
7091
|
7266
|
7323
|
7457
|
7548
|
7688
|
7714
|
7788
|
7829
|
7938
|
8060
|
8066
|
8137
|
8862
|
8864