package C4::AuthoritiesMarc;
+
# Copyright 2000-2002 Katipo Communications
#
# 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 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 3 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.
+# 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.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
use strict;
use warnings;
use C4::AuthoritiesMarc::UNIMARC;
use C4::Charset;
use C4::Log;
+use Koha::MetadataRecord::Authority;
+use Koha::Authorities;
+use Koha::Authority::MergeRequest;
+use Koha::Authority::Types;
+use Koha::Authority;
+use Koha::SearchEngine;
+use Koha::SearchEngine::Search;
-use vars qw($VERSION @ISA @EXPORT);
+use vars qw(@ISA @EXPORT);
BEGIN {
- # set the version for version checking
- $VERSION = 3.01;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
&GetTagsLabels
- &GetAuthType
- &GetAuthTypeCode
&GetAuthMARCFromKohaField
&AddAuthority
&DelAuthority
&GetAuthority
&GetAuthorityXML
-
+
&CountUsage
&CountUsageChildren
&SearchAuthorities
- &BuildSummary
- &BuildUnimarcHierarchies
- &BuildUnimarcHierarchy
+ &BuildSummary
+ &BuildAuthHierarchies
+ &BuildAuthHierarchy
+ &GenerateHierarchy
&merge
&FindDuplicateAuthority
my $dbh=C4::Context->dbh;
return 0, 0 unless $kohafield;
$authtypecode="" unless $authtypecode;
- my $marcfromkohafield;
my $sth = $dbh->prepare("select tagfield,tagsubfield from auth_subfield_structure where kohafield= ? and authtypecode=? ");
$sth->execute($kohafield,$authtypecode);
my ($tagfield,$tagsubfield) = $sth->fetchrow;
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')) {
-
- #
- # build the query
- #
- my $query;
- my @auths=split / /,$authtypecode ;
- foreach my $auth (@auths){
- $query .="AND auth_type= $auth ";
- }
- $query =~ s/^AND //;
- my $dosearch;
- for(my $i = 0 ; $i <= $#{$value} ; $i++)
- {
- if (@$value[$i]){
- if (@$tags[$i] =~/mainentry|mainmainentry/) {
- $query .= qq( AND @$tags[$i] );
- } else {
- $query .=" AND ";
- }
- if (@$operator[$i] eq 'is') {
- $query.=(@$tags[$i]?"=":""). '"'.@$value[$i].'"';
- }elsif (@$operator[$i] eq "="){
- $query.=(@$tags[$i]?"=":""). '"'.@$value[$i].'"';
- }elsif (@$operator[$i] eq "start"){
- $query.=(@$tags[$i]?"=":"").'"'.@$value[$i].'%"';
- } else {
- $query.=(@$tags[$i]?"=":"").'"'.@$value[$i].'%"';
- }
- $dosearch=1;
- }#if value
- }
- #
- # do the query (if we had some search term
- #
- if ($dosearch) {
-# warn "QUERY : $query";
- my $result = C4::Search::NZanalyse($query,'authorityserver');
-# warn "result : $result";
- my %result;
- foreach (split /;/,$result) {
- my ($authid,$title) = split /,/,$_;
- # hint : the result is sorted by title.biblionumber because we can have X biblios with the same title
- # and we don't want to get only 1 result for each of them !!!
- # hint & speed improvement : we can order without reading the record
- # so order, and read records only for the requested page !
- $result{$title.$authid}=$authid;
- }
- # sort the hash and return the same structure as GetRecords (Zebra querying)
- my @listresult = ();
- my $numbers=0;
- if ($sortby eq 'HeadingDsc') { # sort by mainmainentry desc
- foreach my $key (sort {$b cmp $a} (keys %result)) {
- push @listresult, $result{$key};
-# warn "push..."$#finalresult;
- $numbers++;
- }
- } else { # sort by mainmainentry ASC
- foreach my $key (sort (keys %result)) {
- push @listresult, $result{$key};
-# warn "push..."$#finalresult;
- $numbers++;
- }
- }
- # limit the $results_per_page to result size if it's more
- $length = $numbers-$offset if $numbers < ($offset+$length);
- # for the requested page, replace authid by the complete record
- # speed improvement : avoid reading too much things
- my @finalresult;
- for (my $counter=$offset;$counter<=$offset+$length-1;$counter++) {
-# $finalresult[$counter] = GetAuthority($finalresult[$counter])->as_usmarc;
- my $separator=C4::Context->preference('authoritysep');
- my $authrecord =GetAuthority($listresult[$counter]);
- my $authid=$listresult[$counter];
- 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 %newline;
- $newline{used}=CountUsage($authid);
- $newline{summary} = $summary;
- $newline{authid} = $authid;
- $newline{even} = $counter % 2;
- push @finalresult, \%newline;
- }
- return (\@finalresult, $numbers);
- } else {
- return;
- }
- } else {
- my $query;
- my $attr;
- # the marclist may contain "mainentry". In this case, search the tag_to_report, that depends on
- # the authtypecode. Then, search on $a of this tag_to_report
- # also store main entry MARC tag, to extract it at end of search
- my $mainentrytag;
- ##first set the authtype search and may be multiple authorities
+ $sortby="" unless $sortby;
+ my $query;
+ my $qpquery = '';
+ my $QParser;
+ $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser'));
+ my $attr = '';
+ # the marclist may contain "mainentry". In this case, search the tag_to_report, that depends on
+ # the authtypecode. Then, search on $a of this tag_to_report
+ # also store main entry MARC tag, to extract it at end of search
+ my $mainentrytag;
+ ##first set the authtype search and may be multiple authorities
+ if ($authtypecode) {
my $n=0;
my @authtypecode;
my @auths=split / /,$authtypecode ;
foreach my $auth (@auths){
$query .=" \@attr 1=authtype \@attr 5=100 ".$auth; ##No truncation on authtype
- push @authtypecode ,$auth;
+ push @authtypecode ,$auth;
$n++;
}
if ($n>1){
while ($n>1){$query= "\@or ".$query;$n--;}
}
-
- my $dosearch;
- my $and=" \@and " ;
- my $q2;
- my $attr_cnt = 0;
- for(my $i = 0 ; $i <= $#{$value} ; $i++)
- {
- if (@$value[$i]){
+ if ($QParser) {
+ $qpquery .= '(authtype:' . join('|| authtype:', @auths) . ')';
+ }
+ }
+
+ my $dosearch;
+ my $and=" \@and " ;
+ my $q2;
+ my $attr_cnt = 0;
+ for ( my $i = 0 ; $i <= $#{$value} ; $i++ ) {
+ if ( @$value[$i] ) {
+ if ( @$tags[$i] ) {
if ( @$tags[$i] eq "mainmainentry" ) {
$attr = " \@attr 1=Heading-Main ";
}
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 "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 { # Assume any if no index was specified
+ $attr = " \@attr 1=Any ";
}
- else {
- $attr .= " \@attr 5=1 \@attr 4=6 "
- ; ## Word list, right truncated, anywhere
+ } #if @$tags[$i]
+ else { # Assume any if no index was specified
+ $attr = " \@attr 1=Any ";
+ }
+
+ my $operator = @$operator[$i];
+ if ( $operator and $operator eq 'is' ) {
+ $attr .= " \@attr 4=1 \@attr 5=100 "
+ ; ##Phrase, No truncation,all of subfield field must match
+ }
+ elsif ( $operator and $operator eq "=" ) {
+ $attr .= " \@attr 4=107 "; #Number Exact match
+ }
+ elsif ( $operator and $operator eq "start" ) {
+ $attr .= " \@attr 3=2 \@attr 4=1 \@attr 5=1 "
+ ; #Firstinfield Phrase, Right truncated
+ }
+ elsif ( $operator and $operator 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
+ if ( $sortby eq 'Relevance' ) {
+ $attr .= "\@attr 2=102 ";
}
- @$value[$i] =~ s/"/\\"/g; # Escape the double-quotes in the search value
- $attr =$attr."\"".@$value[$i]."\"";
- $q2 .=$attr;
- $dosearch=1;
- ++$attr_cnt;
- }#if value
- }
- ##Add how many queries generated
- if (defined $query && $query=~/\S+/){
- $query= $and x $attr_cnt . $query . (defined $q2 ? $q2 : '');
- } else {
- $query= $q2;
+ }
+ @$value[$i] =~
+ s/"/\\"/g; # Escape the double-quotes in the search value
+ $attr = $attr . "\"" . @$value[$i] . "\"";
+ $q2 .= $attr;
+ $dosearch = 1;
+ ++$attr_cnt;
+ if ($QParser) {
+ $qpquery .= " $tags->[$i]:\"$value->[$i]\"";
+ }
+ } #if value
+ }
+ ##Add how many queries generated
+ if (defined $query && $query=~/\S+/){
+ $query= $and x $attr_cnt . $query . (defined $q2 ? $q2 : '');
+ } else {
+ $query= $q2;
+ }
+ ## Adding order
+ #$query=' @or @attr 7=2 @attr 1=Heading 0 @or @attr 7=1 @attr 1=Heading 1'.$query if ($sortby eq "HeadingDsc");
+ my $orderstring;
+ if ($sortby eq 'HeadingAsc') {
+ $orderstring = '@attr 7=1 @attr 1=Heading 0';
+ } elsif ($sortby eq 'HeadingDsc') {
+ $orderstring = '@attr 7=2 @attr 1=Heading 0';
+ } elsif ($sortby eq 'AuthidAsc') {
+ $orderstring = '@attr 7=1 @attr 4=109 @attr 1=Local-Number 0';
+ } elsif ($sortby eq 'AuthidDsc') {
+ $orderstring = '@attr 7=2 @attr 4=109 @attr 1=Local-Number 0';
+ }
+ if ($QParser) {
+ $qpquery .= ' all:all' unless $value->[0];
+
+ if ( $value->[0] =~ m/^qp=(.*)$/ ) {
+ $qpquery = $1;
}
- ## Adding order
- #$query=' @or @attr 7=2 @attr 1=Heading 0 @or @attr 7=1 @attr 1=Heading 1'.$query if ($sortby eq "HeadingDsc");
- my $orderstring= ($sortby eq "HeadingAsc"?
- '@attr 7=1 @attr 1=Heading 0'
- :
- $sortby eq "HeadingDsc"?
- '@attr 7=2 @attr 1=Heading 0'
- :''
- );
+
+ $qpquery .= " #$sortby" unless $sortby eq '';
+
+ $QParser->parse( $qpquery );
+ $query = $QParser->target_syntax('authorityserver');
+ } else {
$query=($query?$query:"\@attr 1=_ALLRECORDS \@attr 2=103 ''");
$query="\@or $orderstring $query" if $orderstring;
+ }
- $offset=0 unless $offset;
- my $counter = $offset;
- $length=10 unless $length;
- my @oAuth;
- my $i;
- $oAuth[0]=C4::Context->Zconn("authorityserver" , 1);
- my $Anewq= new ZOOM::Query::PQF($query,$oAuth[0]);
- my $oAResult;
- $oAResult= $oAuth[0]->search($Anewq) ;
- while (($i = ZOOM::event(\@oAuth)) != 0) {
- my $ev = $oAuth[$i-1]->last_event();
- last if $ev == ZOOM::Event::ZEND;
- }
- my($error, $errmsg, $addinfo, $diagset) = $oAuth[0]->error_x();
- if ($error) {
- warn "oAuth error: $errmsg ($error) $addinfo $diagset\n";
- goto NOLUCK;
- }
-
- my $nbresults;
- $nbresults=$oAResult->size();
- my $nremains=$nbresults;
- my @result = ();
- my @finalresult = ();
-
- if ($nbresults>0){
+ $offset = 0 if not defined $offset or $offset < 0;
+ my $counter = $offset;
+ $length=10 unless $length;
+ my @oAuth;
+ my $i;
+ $oAuth[0]=C4::Context->Zconn("authorityserver" , 1);
+ my $Anewq= new ZOOM::Query::PQF($query,$oAuth[0]);
+ my $oAResult;
+ $oAResult= $oAuth[0]->search($Anewq) ;
+ while (($i = ZOOM::event(\@oAuth)) != 0) {
+ my $ev = $oAuth[$i-1]->last_event();
+ last if $ev == ZOOM::Event::ZEND;
+ }
+ my($error, $errmsg, $addinfo, $diagset) = $oAuth[0]->error_x();
+ if ($error) {
+ warn "oAuth error: $errmsg ($error) $addinfo $diagset\n";
+ goto NOLUCK;
+ }
+
+ my $nbresults;
+ $nbresults=$oAResult->size();
+ my $nremains=$nbresults;
+ my @result = ();
+ my @finalresult = ();
+
+ if ($nbresults>0){
+
+ ##Find authid and linkid fields
+ ##we may be searching multiple authoritytypes.
+ ## FIXME this assumes that all authid and linkid fields are the same for all authority types
+ # my ($authidfield,$authidsubfield)=GetAuthMARCFromKohaField($dbh,"auth_header.authid",$authtypecode[0]);
+ # my ($linkidfield,$linkidsubfield)=GetAuthMARCFromKohaField($dbh,"auth_header.linkid",$authtypecode[0]);
+ while (($counter < $nbresults) && ($counter < ($offset + $length))) {
- ##Find authid and linkid fields
- ##we may be searching multiple authoritytypes.
- ## FIXME this assumes that all authid and linkid fields are the same for all authority types
- # my ($authidfield,$authidsubfield)=GetAuthMARCFromKohaField($dbh,"auth_header.authid",$authtypecode[0]);
- # my ($linkidfield,$linkidsubfield)=GetAuthMARCFromKohaField($dbh,"auth_header.linkid",$authtypecode[0]);
- while (($counter < $nbresults) && ($counter < ($offset + $length))) {
-
- ##Here we have to extract MARC record and $authid from ZEBRA AUTHORITIES
- my $rec=$oAResult->record($counter);
- my $marcdata=$rec->raw();
- my $authrecord;
- my $separator=C4::Context->preference('authoritysep');
- $authrecord = MARC::File::USMARC::decode($marcdata);
- my $authid=$authrecord->field('001')->data();
- my %newline;
- $newline{authid} = $authid;
- 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];
- }
+ ##Here we have to extract MARC record and $authid from ZEBRA AUTHORITIES
+ my $rec=$oAResult->record($counter);
+ my $separator=C4::Context->preference('AuthoritySeparator');
+ my $authrecord = C4::Search::new_record_from_zebra(
+ 'authorityserver',
+ $rec->raw()
+ );
+
+ if ( !defined $authrecord or !defined $authrecord->field('001') ) {
+ $counter++;
+ next;
+ }
+
+ SetUTF8Flag( $authrecord );
+
+ my $authid=$authrecord->field('001')->data();
+ my %newline;
+ $newline{authid} = $authid;
+ if ( !$skipmetadata ) {
+ my $auth_tag_to_report;
+ $auth_tag_to_report = Koha::Authority::Types->find($authtypecode)->auth_tag_to_report
+ if $authtypecode;
+ 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
- ###
- if (! $skipmetadata) {
- for (my $z=0; $z<@finalresult; $z++){
- my $count=CountUsage($finalresult[$z]{authid});
- $finalresult[$z]{used}=$count;
- }# all $z's
+
+ my ( $thisauthtype, $thisauthtypecode );
+ if ( my $authority = Koha::Authorities->find($authid) ) {
+ $thisauthtypecode = $authority->authtypecode;
+ $thisauthtype = Koha::Authority::Types->find($thisauthtypecode);
}
+ unless (defined $thisauthtype) {
+ $thisauthtypecode = $authtypecode;
+ $thisauthtype = Koha::Authority::Types->find($thisauthtypecode);
+ }
+ my $summary = BuildSummary( $authrecord, $authid, $thisauthtypecode );
- }## if nbresult
- NOLUCK:
- $oAResult->destroy();
- # $oAuth[0]->destroy();
-
- return (\@finalresult, $nbresults);
- }
+ $newline{authtype} = defined($thisauthtype) ?
+ $thisauthtype->authtypetext : '';
+ $newline{summary} = $summary;
+ $newline{even} = $counter % 2;
+ $newline{reported_tag} = $reported_tag;
+ }
+ $counter++;
+ push @finalresult, \%newline;
+ }## while counter
+ ###
+ 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();
+ # $oAuth[0]->destroy();
+
+ return (\@finalresult, $nbresults);
}
=head2 CountUsage
sub CountUsage {
my ($authid) = @_;
- if (C4::Context->preference('NoZebra')) {
- # Read the index Koha-Auth-Number for this authid and count the lines
- my $result = C4::Search::NZanalyse("an=$authid");
- my @tab = split /;/,$result;
- return scalar @tab;
- } else {
### ZOOM search here
my $query;
- $query= "an=".$authid;
- my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10);
+ $query= "an:".$authid;
+ # Should really be replaced with a real count call, this is a
+ # bad way.
+ my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
+ my ($err,$res,$result) = $searcher->simple_search_compat($query,0,1);
if ($err) {
warn "Error: $err from search $query";
$result = 0;
}
return $result;
- }
}
=head2 CountUsageChildren
my ($authid) = @_;
}
-=head2 GetAuthTypeCode
-
- $authtypecode= &GetAuthTypeCode($authid)
-
-returns authtypecode of an authid
-
-=cut
-
-sub GetAuthTypeCode {
-#AUTHfind_authtypecode
- my ($authid) = @_;
- my $dbh=C4::Context->dbh;
- my $sth = $dbh->prepare("select authtypecode from auth_header where authid=?");
- $sth->execute($authid);
- my $authtypecode = $sth->fetchrow;
- return $authtypecode;
-}
-
=head2 GuessAuthTypeCode
my $authtypecode = GuessAuthTypeCode($record);
=cut
sub GuessAuthTypeCode {
- my ($record) = @_;
+ my ($record, $heading_fields) = @_;
return unless defined $record;
-my $heading_fields = {
+ $heading_fields //= {
"MARC21"=>{
'100'=>{authtypecode=>'PERSO_NAME'},
'110'=>{authtypecode=>'CORPO_NAME'},
$res->{$tag}->{repeatable} = $repeatable;
}
$sth= $dbh->prepare(
-"SELECT tagfield,tagsubfield,liblibrarian,libopac,tab, mandatory, repeatable,authorised_value,frameworkcode as authtypecode,value_builder,kohafield,seealso,hidden,isurl
+"SELECT tagfield,tagsubfield,liblibrarian,libopac,tab, mandatory, repeatable,authorised_value,frameworkcode as authtypecode,value_builder,kohafield,seealso,hidden,isurl,defaultvalue
FROM auth_subfield_structure
WHERE authtypecode=?
ORDER BY tagfield,tagsubfield"
my $hidden;
my $isurl;
my $link;
+ my $defaultvalue;
while (
( $tag, $subfield, $liblibrarian, , $libopac, $tab,
$mandatory, $repeatable, $authorised_value, $authtypecode,
$value_builder, $kohafield, $seealso, $hidden,
- $isurl, $link )
+ $isurl, $defaultvalue, $link )
= $sth->fetchrow
)
{
$res->{$tag}->{$subfield}->{hidden} = $hidden;
$res->{$tag}->{$subfield}->{isurl} = $isurl;
$res->{$tag}->{$subfield}->{link} = $link;
+ $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue;
}
return $res;
}
if ($format eq "UNIMARCAUTH") {
$record->leader(" nx j22 ") unless ($record->leader());
- my $date=POSIX::strftime("%Y%m%d",localtime);
+ my $date=POSIX::strftime("%Y%m%d",localtime);
+ my $defaultfield100 = C4::Context->preference('UNIMARCAuthorityField100');
if (my $string=$record->subfield('100',"a")){
$string=~s/fre50/frey50/;
$record->field('100')->update('a'=>$string);
}
elsif ($record->field('100')){
- $record->field('100')->update('a'=>$date."afrey50 ba0");
+ $record->field('100')->update('a'=>$date.$defaultfield100);
} else {
$record->append_fields(
MARC::Field->new('100',' ',' '
- ,'a'=>$date."afrey50 ba0")
+ ,'a'=>$date.$defaultfield100)
);
}
}
$record->add_fields($auth_type_tag,'','', $auth_type_subfield=>$authtypecode);
}
- my $auth_exists=0;
- my $oldRecord;
- if (!$authid) {
- my $sth=$dbh->prepare("select max(authid) from auth_header");
- $sth->execute;
- ($authid)=$sth->fetchrow;
- $authid=$authid+1;
- ##Insert the recordID in MARC record
- unless ($record->field('001') && $record->field('001')->data() eq $authid){
- $record->delete_field($record->field('001'));
- $record->insert_fields_ordered(MARC::Field->new('001',$authid));
+ # Save record into auth_header, update 001
+ if (!$authid ) {
+ # Save a blank record, get authid
+ $dbh->do( "INSERT INTO auth_header (datecreated,marcxml) values (NOW(),?)", undef, '' );
+ $authid = $dbh->last_insert_id( undef, undef, 'auth_header', 'authid' );
+ logaction( "AUTHORITIES", "ADD", $authid, "authority" ) if C4::Context->preference("AuthoritiesLog");
}
- } else {
- $auth_exists=$dbh->do(qq(select authid from auth_header where authid=?),undef,$authid);
-# warn "auth_exists = $auth_exists";
- }
- if ($auth_exists>0){
- $oldRecord=GetAuthority($authid);
- $record->add_fields('001',$authid) unless ($record->field('001'));
-# warn "\n\n\n enregistrement".$record->as_formatted;
- my $sth=$dbh->prepare("update auth_header set authtypecode=?,marc=?,marcxml=? where authid=?");
- $sth->execute($authtypecode,$record->as_usmarc,$record->as_xml_record($format),$authid) or die $sth->errstr;
- $sth->finish;
- }
- else {
- my $sth=$dbh->prepare("insert into auth_header (authid,datecreated,authtypecode,marc,marcxml) values (?,now(),?,?,?)");
- $sth->execute($authid,$authtypecode,$record->as_usmarc,$record->as_xml_record($format));
- $sth->finish;
- logaction( "AUTHORITIES", "ADD", $authid, "authority" ) if C4::Context->preference("AuthoritiesLog");
- }
- ModZebra($authid,'specialUpdate',"authorityserver",$oldRecord,$record);
- return ($authid);
+ # Insert/update the recordID in MARC record
+ $record->delete_field( $record->field('001') );
+ $record->insert_fields_ordered( MARC::Field->new( '001', $authid ) );
+ # Update
+ $dbh->do( "UPDATE auth_header SET authtypecode=?, marc=?, marcxml=? WHERE authid=?", undef, $authtypecode, $record->as_usmarc, $record->as_xml_record($format), $authid ) or die $DBI::errstr;
+ ModZebra( $authid, 'specialUpdate', 'authorityserver', $record );
+
+ return ( $authid );
}
-
=head2 DelAuthority
- $authid= &DelAuthority($authid)
+ DelAuthority({ authid => $authid, [ skip_merge => 1 ] });
-Deletes $authid
+Deletes $authid and calls merge to cleanup linked biblio records.
+Parameter skip_merge is used in authorities/merge.pl. You should normally not
+use it.
=cut
sub DelAuthority {
- my ($authid) = @_;
- my $dbh=C4::Context->dbh;
-
+ my ( $params ) = @_;
+ my $authid = $params->{authid} || return;
+ my $skip_merge = $params->{skip_merge};
+ my $dbh = C4::Context->dbh;
+ merge({ mergefrom => $authid }) if !$skip_merge;
+ $dbh->do( "DELETE FROM auth_header WHERE authid=?", undef, $authid );
logaction( "AUTHORITIES", "DELETE", $authid, "authority" ) if C4::Context->preference("AuthoritiesLog");
- ModZebra($authid,"recordDelete","authorityserver",GetAuthority($authid),undef);
- my $sth = $dbh->prepare("DELETE FROM auth_header WHERE authid=?");
- $sth->execute($authid);
+ ModZebra( $authid, "recordDelete", "authorityserver", undef);
}
=head2 ModAuthority
=cut
sub ModAuthority {
- my ($authid,$record,$authtypecode)=@_; # deprecated $merge parameter removed
-
- my $dbh=C4::Context->dbh;
- #Now rewrite the $record to table with an add
- my $oldrecord=GetAuthority($authid);
- $authid=AddAuthority($record,$authid,$authtypecode);
-
- # If a library thinks that updating all biblios is a long process and wishes
- # to leave that to a cron job, use misc/migration_tools/merge_authority.pl.
- # In that case set system preference "dontmerge" to 1. Otherwise biblios will
- # be updated.
- unless(C4::Context->preference('dontmerge') eq '1'){
- &merge($authid,$oldrecord,$authid,$record);
- } else {
- # save a record in need_merge_authorities table
- my $sqlinsert="INSERT INTO need_merge_authorities (authid, done) ".
- "VALUES (?,?)";
- $dbh->do($sqlinsert,undef,($authid,0));
- }
- logaction( "AUTHORITIES", "MODIFY", $authid, "BEFORE=>" . $oldrecord->as_formatted ) if C4::Context->preference("AuthoritiesLog");
- return $authid;
+ my ( $authid, $record, $authtypecode ) = @_;
+ my $oldrecord = GetAuthority($authid);
+ #Now rewrite the $record to table with an add
+ $authid = AddAuthority($record, $authid, $authtypecode);
+ merge({ mergefrom => $authid, MARCfrom => $oldrecord, mergeto => $authid, MARCto => $record });
+ logaction( "AUTHORITIES", "MODIFY", $authid, "authority BEFORE=>" . $oldrecord->as_formatted ) if C4::Context->preference("AuthoritiesLog");
+ return $authid;
}
=head2 GetAuthorityXML
sub GetAuthority {
my ($authid)=@_;
- my $dbh=C4::Context->dbh;
- my $sth=$dbh->prepare("select authtypecode, marcxml from auth_header where authid=?");
- $sth->execute($authid);
- my ($authtypecode, $marcxml) = $sth->fetchrow;
- my $record=eval {MARC::Record->new_from_xml(StripNonXmlChars($marcxml),'UTF-8',
- (C4::Context->preference("marcflavour") eq "UNIMARC"?"UNIMARCAUTH":C4::Context->preference("marcflavour")))};
- return undef if ($@);
- $record->encoding('UTF-8');
- if (C4::Context->preference("marcflavour") eq "MARC21") {
- my ($auth_type_tag, $auth_type_subfield) = get_auth_type_location($authtypecode);
- C4::AuthoritiesMarc::MARC21::fix_marc21_auth_type_location($record, $auth_type_tag, $auth_type_subfield);
- }
- return ($record);
-}
-
-=head2 GetAuthType
-
- $result = &GetAuthType($authtypecode)
-
-If the authority type specified by C<$authtypecode> exists,
-returns a hashref of the type's fields. If the type
-does not exist, returns undef.
-
-=cut
-
-sub GetAuthType {
- my ($authtypecode) = @_;
- my $dbh=C4::Context->dbh;
- my $sth;
- if (defined $authtypecode){ # NOTE - in MARC21 framework, '' is a valid authority
- # type (FIXME but why?)
- $sth=$dbh->prepare("select * from auth_types where authtypecode=?");
- $sth->execute($authtypecode);
- if (my $res = $sth->fetchrow_hashref) {
- return $res;
- }
- }
- return;
+ my $authority = Koha::MetadataRecord::Authority->get_from_authid($authid);
+ return unless $authority;
+ return ($authority->record);
}
-
=head2 FindDuplicateAuthority
$record= &FindDuplicateAuthority( $record, $authtypecode)
# warn "IN for ".$record->as_formatted;
my $dbh = C4::Context->dbh;
# warn "".$record->as_formatted;
- my $sth = $dbh->prepare("select auth_tag_to_report from auth_types where authtypecode=?");
- $sth->execute($authtypecode);
- my ($auth_tag_to_report) = $sth->fetchrow;
- $sth->finish;
+ my $auth_tag_to_report = Koha::Authority::Types->find($authtypecode)->auth_tag_to_report;
# warn "record :".$record->as_formatted." auth_tag_to_report :$auth_tag_to_report";
# build a request for SearchAuthorities
- my $query='at='.$authtypecode.' ';
- my $filtervalues=qr([\001-\040\!\'\"\`\#\$\%\&\*\+,\-\./:;<=>\?\@\(\)\{\[\]\}_\|\~]);
+ my $QParser;
+ $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser'));
+ my $op;
+ if ($QParser) {
+ $op = '&&';
+ } else {
+ $op = 'and';
+ }
+ my $query='at:'.$authtypecode.' ';
+ my $filtervalues=qr([\001-\040\Q!'"`#$%&*+,-./:;<=>?@(){[}_|~\E\]]);
if ($record->field($auth_tag_to_report)) {
- foreach ($record->field($auth_tag_to_report)->subfields()) {
- $_->[1]=~s/$filtervalues/ /g; $query.= " and he,wrdl=\"".$_->[1]."\"" if ($_->[0]=~/[A-z]/);
- }
+ foreach ($record->field($auth_tag_to_report)->subfields()) {
+ $_->[1]=~s/$filtervalues/ /g; $query.= " $op he:\"".$_->[1]."\"" if ($_->[0]=~/[A-z]/);
+ }
}
- my ($error, $results, $total_hits) = C4::Search::SimpleSearch( $query, 0, 1, [ "authorityserver" ] );
+ my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::AUTHORITIES_INDEX});
+ my ($error, $results, $total_hits) = $searcher->simple_search_compat( $query, 0, 1 );
# there is at least 1 result => return the 1st one
if (!defined $error && @{$results} ) {
- my $marcrecord = MARC::File::USMARC::decode($results->[0]);
- return $marcrecord->field('001')->data,BuildSummary($marcrecord,$marcrecord->field('001')->data,$authtypecode);
+ my $marcrecord = C4::Search::new_record_from_zebra(
+ 'authorityserver',
+ $results->[0]
+ );
+ return $marcrecord->field('001')->data,BuildSummary($marcrecord,$marcrecord->field('001')->data,$authtypecode);
}
# no result, returns nothing
return;
=head2 BuildSummary
- $text= &BuildSummary( $record, $authid, $authtypecode)
+ $summary= &BuildSummary( $record, $authid, $authtypecode)
-return HTML encoded Summary
+Returns a hashref with a summary of the specified record.
-Comment : authtypecode can be infered from both record and authid.
+Comment : authtypecode can be inferred from both record and authid.
Moreover, authid can also be inferred from $record.
Would it be interesting to delete those things.
=cut
-sub BuildSummary{
-## give this a Marc record to return summary
- my ($record,$authid,$authtypecode)=@_;
- my $dbh=C4::Context->dbh;
- my $summary;
- # handle $authtypecode is NULL or eq ""
- if ($authtypecode) {
- my $authref = GetAuthType($authtypecode);
- $summary = $authref->{summary};
- }
- # FIXME: should use I18N.pm
- my %language;
- $language{'fre'}="Français";
- $language{'eng'}="Anglais";
- $language{'ger'}="Allemand";
- $language{'ita'}="Italien";
- $language{'spa'}="Espagnol";
- my %thesaurus;
- $thesaurus{'1'}="Peuples";
- $thesaurus{'2'}="Anthroponymes";
- $thesaurus{'3'}="Oeuvres";
- $thesaurus{'4'}="Chronologie";
- $thesaurus{'5'}="Lieux";
- $thesaurus{'6'}="Sujets";
- #thesaurus a remplir
- my @fields = $record->fields();
- my $reported_tag;
- # if the library has a summary defined, use it. Otherwise, build a standard one
- # FIXME - it appears that the summary field in the authority frameworks
- # can work as a display template. However, this doesn't
- # suit the MARC21 version, so for now the "templating"
- # feature will be enabled only for UNIMARC for backwards
- # compatibility.
- if ($summary and C4::Context->preference('marcflavour') eq 'UNIMARC') {
- my @fields = $record->fields();
- # $reported_tag = '$9'.$result[$counter];
- my @stringssummary;
- foreach my $field (@fields) {
- my $tag = $field->tag();
- my $tagvalue = $field->as_string();
- my $localsummary= $summary;
- $localsummary =~ s/\[(.?.?.?.?)$tag\*(.*?)\]/$1$tagvalue$2\[$1$tag$2\]/g;
- if ($tag<10) {
- if ($tag eq '001') {
- $reported_tag.='$3'.$field->data();
+sub BuildSummary {
+ ## give this a Marc record to return summary
+ my ($record,$authid,$authtypecode)=@_;
+ my $dbh=C4::Context->dbh;
+ my %summary;
+ my $summary_template;
+ # handle $authtypecode is NULL or eq ""
+ if ($authtypecode) {
+ my $authref = Koha::Authority::Types->find($authtypecode);
+ if ( $authref ) {
+ $summary{authtypecode} = $authref->authtypecode;
+ $summary{type} = $authref->authtypetext;
+ $summary_template = $authref->summary;
+ # for MARC21, the authority type summary displays a label meant for
+ # display
+ if (C4::Context->preference('marcflavour') ne 'UNIMARC') {
+ $summary{label} = $authref->summary;
+ } else {
+ $summary{summary} = $authref->summary;
+ }
}
- } else {
- my @subf = $field->subfields;
- for my $i (0..$#subf) {
- my $subfieldcode = $subf[$i][0];
- my $subfieldvalue = $subf[$i][1];
- my $tagsubf = $tag.$subfieldcode;
- $localsummary =~ s/\[(.?.?.?.?)$tagsubf(.*?)\]/$1$subfieldvalue$2\[$1$tagsubf$2\]/g;
+ }
+ my $marc21subfields = 'abcdfghjklmnopqrstuvxyz68';
+ my %marc21controlrefs = ( 'a' => 'earlier',
+ 'b' => 'later',
+ 'd' => 'acronym',
+ 'f' => 'musical',
+ 'g' => 'broader',
+ 'h' => 'narrower',
+ 'n' => 'notapplicable',
+ 'i' => 'subfi',
+ 't' => 'parent'
+ );
+ my %unimarc_relation_from_code = (
+ g => 'broader',
+ h => 'narrower',
+ a => 'seealso',
+ );
+ my %thesaurus;
+ $thesaurus{'1'}="Peuples";
+ $thesaurus{'2'}="Anthroponymes";
+ $thesaurus{'3'}="Oeuvres";
+ $thesaurus{'4'}="Chronologie";
+ $thesaurus{'5'}="Lieux";
+ $thesaurus{'6'}="Sujets";
+ #thesaurus a remplir
+ my $reported_tag;
+# if the library has a summary defined, use it. Otherwise, build a standard one
+# FIXME - it appears that the summary field in the authority frameworks
+# can work as a display template. However, this doesn't
+# suit the MARC21 version, so for now the "templating"
+# feature will be enabled only for UNIMARC for backwards
+# compatibility.
+ if ($summary{summary} and C4::Context->preference('marcflavour') eq 'UNIMARC') {
+ my @matches = ($summary{summary} =~ m/\[(.*?)(\d{3})([\*a-z0-9])(.*?)\]/g);
+ my (@textbefore, @tag, @subtag, @textafter);
+ for(my $i = 0; $i < scalar @matches; $i++){
+ push @textbefore, $matches[$i] if($i%4 == 0);
+ push @tag, $matches[$i] if($i%4 == 1);
+ push @subtag, $matches[$i] if($i%4 == 2);
+ push @textafter, $matches[$i] if($i%4 == 3);
+ }
+ for(my $i = scalar @tag; $i >= 0; $i--){
+ my $textbefore = $textbefore[$i] || '';
+ my $tag = $tag[$i] || '';
+ my $subtag = $subtag[$i] || '';
+ my $textafter = $textafter[$i] || '';
+ my $value = '';
+ my $field = $record->field($tag);
+ if ( $field ) {
+ if($subtag eq '*') {
+ if($tag < 10) {
+ $value = $textbefore . $field->data() . $textafter;
+ }
+ } else {
+ my @subfields = $field->subfield($subtag);
+ if(@subfields > 0) {
+ $value = $textbefore . join (" - ", @subfields) . $textafter;
+ }
+ }
+ }
+ $summary{summary} =~ s/\[\Q$textbefore$tag$subtag$textafter\E\]/$value/;
}
- }
- push @stringssummary, $localsummary if ($localsummary ne $summary);
+ $summary{summary} =~ s/\\n/<br \/>/g;
}
- my $resultstring;
- $resultstring = join(" -- ",@stringssummary);
- $resultstring =~ s/\[(.*?)\]//g;
- $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 @fields = $record->fields();
+ my @authorized;
+ my @notes;
+ my @seefrom;
+ my @seealso;
+ my @otherscript;
if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
- # construct UNIMARC summary, that is quite different from MARC21 one
- # accepted form
- foreach my $field ($record->field('2..')) {
- $heading.= $field->as_string('abcdefghijlmnopqrstuvwxyz');
- }
- # rejected form(s)
- foreach my $field ($record->field('3..')) {
- $notes.= '<span class="note">'.$field->subfield('a')."</span>\n";
- }
- foreach my $field ($record->field('4..')) {
- if ($field->subfield('2')) {
- my $thesaurus = "thes. : ".$thesaurus{"$field->subfield('2')"}." : ";
- $see.= '<span class="UF">'.$thesaurus.$field->as_string('abcdefghijlmnopqrstuvwxyz')."</span> -- \n";
+# construct UNIMARC summary, that is quite different from MARC21 one
+# accepted form
+ foreach my $field ($record->field('2..')) {
+ push @authorized, {
+ heading => $field->as_string('abcdefghijlmnopqrstuvwxyz'),
+ hemain => ( $field->subfield('a') // undef ),
+ field => $field->tag(),
+ };
}
- }
- # see :
- foreach my $field ($record->field('5..')) {
-
- if (($field->subfield('5')) && ($field->subfield('a')) && ($field->subfield('5') eq 'g')) {
- $broaderterms.= '<span class="BT"> '.$field->as_string('abcdefgjxyz')."</span> -- \n";
- } elsif (($field->subfield('5')) && ($field->as_string) && ($field->subfield('5') eq 'h')){
- $narrowerterms.= '<span class="NT">'.$field->as_string('abcdefgjxyz')."</span> -- \n";
- } elsif ($field->subfield('a')) {
- $seealso.= '<span class="RT">'.$field->as_string('abcdefgxyz')."</a></span> -- \n";
+# rejected form(s)
+ foreach my $field ($record->field('3..')) {
+ push @notes, { note => $field->subfield('a'), field => $field->tag() };
}
- }
- # // form
- foreach my $field ($record->field('7..')) {
- my $lang = substr($field->subfield('8'),3,3);
- $seeheading.= '<span class="langue"> En '.$language{$lang}.' : </span><span class="OT"> '.$field->subfield('a')."</span><br />\n";
- }
- $broaderterms =~s/-- \n$//;
- $narrowerterms =~s/-- \n$//;
- $seealso =~s/-- \n$//;
- $see =~s/-- \n$//;
- $summary = $heading."<br />".($notes?"$notes <br />":"");
- $summary.= '<p><div class="label">TG : '.$broaderterms.'</div></p>' if ($broaderterms);
- $summary.= '<p><div class="label">TS : '.$narrowerterms.'</div></p>' if ($narrowerterms);
- $summary.= '<p><div class="label">TA : '.$seealso.'</div></p>' if ($seealso);
- $summary.= '<p><div class="label">EP : '.$see.'</div></p>' if ($see);
- $summary.= '<p><div class="label">'.$seeheading.'</div></p>' if ($seeheading);
- } else {
- # construct MARC21 summary
- # FIXME - looping over 1XX is questionable
- # since MARC21 authority should have only one 1XX
- foreach my $field ($record->field('1..')) {
- next if "152" eq $field->tag(); # FIXME - 152 is not a good tag to use
- # in MARC21 -- purely local tags really ought to be
- # 9XX
- if ($record->field('100')) {
- $heading.= $field->as_string('abcdefghjklmnopqrstvxyz68');
- } elsif ($record->field('110')) {
- $heading.= $field->as_string('abcdefghklmnoprstvxyz68');
- } elsif ($record->field('111')) {
- $heading.= $field->as_string('acdefghklnpqstvxyz68');
- } elsif ($record->field('130')) {
- $heading.= $field->as_string('adfghklmnoprstvxyz68');
- } elsif ($record->field('148')) {
- $heading.= $field->as_string('abvxyz68');
- } elsif ($record->field('150')) {
- $heading.= $field->as_string('abvxyz68');
- #$heading.= $field->as_formatted();
- my $tag=$field->tag();
- $heading=~s /^$tag//g;
- $heading =~s /\_/\$/g;
- } elsif ($record->field('151')) {
- $heading.= $field->as_string('avxyz68');
- } elsif ($record->field('155')) {
- $heading.= $field->as_string('abvxyz68');
- } elsif ($record->field('180')) {
- $heading.= $field->as_string('vxyz68');
- } elsif ($record->field('181')) {
- $heading.= $field->as_string('vxyz68');
- } elsif ($record->field('182')) {
- $heading.= $field->as_string('vxyz68');
- } elsif ($record->field('185')) {
- $heading.= $field->as_string('vxyz68');
- } else {
- $heading.= $field->as_string();
- }
- } #See From
- foreach my $field ($record->field('4..')) {
- $seeheading.= "<br /> <i>used for/see from:</i> ".$field->as_string();
- } #See Also
- foreach my $field ($record->field('5..')) {
- $altheading.= "<br /> <i>see also:</i> ".$field->as_string();
- }
- $summary .= ": " if $summary;
- $summary.=$heading.$seeheading.$altheading;
- }
- }
- return $summary;
+ foreach my $field ($record->field('4..')) {
+ my $thesaurus = $field->subfield('2') ? "thes. : ".$thesaurus{"$field->subfield('2')"}." : " : '';
+ push @seefrom, {
+ heading => $thesaurus . $field->as_string('abcdefghijlmnopqrstuvwxyz'),
+ hemain => ( $field->subfield('a') // undef ),
+ type => 'seefrom',
+ field => $field->tag(),
+ };
+ }
+
+ # see :
+ @seealso = map {
+ my $type = $unimarc_relation_from_code{$_->subfield('5') || 'a'};
+ my $heading = $_->as_string('abcdefgjxyz');
+ {
+ field => $_->tag,
+ type => $type,
+ heading => $heading,
+ hemain => ( $_->subfield('a') // undef ),
+ search => $heading,
+ authid => ( $_->subfield('9') // undef ),
+ }
+ } $record->field('5..');
+
+ # Other forms
+ @otherscript = map { {
+ lang => length ($_->subfield('8')) == 6 ? substr ($_->subfield('8'), 3, 3) : $_->subfield('8') || '',
+ term => $_->subfield('a') . ($_->subfield('b') ? ', ' . $_->subfield('b') : ''),
+ direction => 'ltr',
+ field => $_->tag,
+ } } $record->field('7..');
+
+ } else {
+# construct MARC21 summary
+# FIXME - looping over 1XX is questionable
+# since MARC21 authority should have only one 1XX
+ my $subfields_to_report;
+ foreach my $field ($record->field('1..')) {
+ my $tag = $field->tag();
+ next if "152" eq $tag;
+# FIXME - 152 is not a good tag to use
+# in MARC21 -- purely local tags really ought to be
+# 9XX
+ if ($tag eq '100') {
+ $subfields_to_report = 'abcdefghjklmnopqrstvxyz';
+ } elsif ($tag eq '110') {
+ $subfields_to_report = 'abcdefghklmnoprstvxyz';
+ } elsif ($tag eq '111') {
+ $subfields_to_report = 'acdefghklnpqstvxyz';
+ } elsif ($tag eq '130') {
+ $subfields_to_report = 'adfghklmnoprstvxyz';
+ } elsif ($tag eq '148') {
+ $subfields_to_report = 'abvxyz';
+ } elsif ($tag eq '150') {
+ $subfields_to_report = 'abvxyz';
+ } elsif ($tag eq '151') {
+ $subfields_to_report = 'avxyz';
+ } elsif ($tag eq '155') {
+ $subfields_to_report = 'abvxyz';
+ } elsif ($tag eq '180') {
+ $subfields_to_report = 'vxyz';
+ } elsif ($tag eq '181') {
+ $subfields_to_report = 'vxyz';
+ } elsif ($tag eq '182') {
+ $subfields_to_report = 'vxyz';
+ } elsif ($tag eq '185') {
+ $subfields_to_report = 'vxyz';
+ }
+ if ($subfields_to_report) {
+ push @authorized, {
+ heading => $field->as_string($subfields_to_report),
+ hemain => ( $field->subfield( substr($subfields_to_report, 0, 1) ) // undef ),
+ field => $tag,
+ };
+ } else {
+ push @authorized, {
+ heading => $field->as_string(),
+ hemain => ( $field->subfield( 'a' ) // undef ),
+ field => $tag,
+ };
+ }
+ }
+ foreach my $field ($record->field('4..')) { #See From
+ my $type = 'seefrom';
+ $type = ($marc21controlrefs{substr $field->subfield('w'), 0, 1} || '') if ($field->subfield('w'));
+ if ($type eq 'notapplicable') {
+ $type = substr $field->subfield('w'), 2, 1;
+ $type = 'earlier' if $type && $type ne 'n';
+ }
+ if ($type eq 'subfi') {
+ push @seefrom, {
+ heading => $field->as_string($marc21subfields),
+ hemain => $field->subfield( substr($marc21subfields, 0, 1) ),
+ type => ($field->subfield('i') || ''),
+ field => $field->tag(),
+ };
+ } else {
+ push @seefrom, {
+ heading => $field->as_string($marc21subfields),
+ hemain => $field->subfield( substr($marc21subfields, 0, 1) ),
+ type => $type,
+ field => $field->tag(),
+ };
+ }
+ }
+ foreach my $field ($record->field('5..')) { #See Also
+ my $type = 'seealso';
+ $type = ($marc21controlrefs{substr $field->subfield('w'), 0, 1} || '') if ($field->subfield('w'));
+ if ($type eq 'notapplicable') {
+ $type = substr $field->subfield('w'), 2, 1;
+ $type = 'earlier' if $type && $type ne 'n';
+ }
+ if ($type eq 'subfi') {
+ push @seealso, {
+ heading => $field->as_string($marc21subfields),
+ hemain => $field->subfield( substr($marc21subfields, 0, 1) ),
+ type => $field->subfield('i'),
+ field => $field->tag(),
+ search => $field->as_string($marc21subfields) || '',
+ authid => $field->subfield('9') || ''
+ };
+ } else {
+ push @seealso, {
+ heading => $field->as_string($marc21subfields),
+ hemain => $field->subfield( substr($marc21subfields, 0, 1) ),
+ type => $type,
+ field => $field->tag(),
+ search => $field->as_string($marc21subfields) || '',
+ authid => $field->subfield('9') || ''
+ };
+ }
+ }
+ foreach my $field ($record->field('6..')) {
+ push @notes, { note => $field->as_string(), field => $field->tag() };
+ }
+ foreach my $field ($record->field('880')) {
+ my $linkage = $field->subfield('6');
+ my $category = substr $linkage, 0, 1;
+ if ($category eq '1') {
+ $category = 'preferred';
+ } elsif ($category eq '4') {
+ $category = 'seefrom';
+ } elsif ($category eq '5') {
+ $category = 'seealso';
+ }
+ my $type;
+ if ($field->subfield('w')) {
+ $type = $marc21controlrefs{substr $field->subfield('w'), '0'};
+ } else {
+ $type = $category;
+ }
+ my $direction = $linkage =~ m#/r$# ? 'rtl' : 'ltr';
+ push @otherscript, { term => $field->as_string($subfields_to_report), category => $category, type => $type, direction => $direction, linkage => $linkage };
+ }
+ }
+ $summary{mainentry} = $authorized[0]->{heading};
+ $summary{mainmainentry} = $authorized[0]->{hemain};
+ $summary{authorized} = \@authorized;
+ $summary{notes} = \@notes;
+ $summary{seefrom} = \@seefrom;
+ $summary{seealso} = \@seealso;
+ $summary{otherscript} = \@otherscript;
+ return \%summary;
+}
+
+=head2 GetAuthorizedHeading
+
+ $heading = &GetAuthorizedHeading({ record => $record, authid => $authid })
+
+Takes a MARC::Record object describing an authority record or an authid, and
+returns a string representation of the first authorized heading. This routine
+should be considered a temporary shim to ease the future migration of authority
+data from C4::AuthoritiesMarc to the object-oriented Koha::*::Authority.
+
+=cut
+
+sub GetAuthorizedHeading {
+ my $args = shift;
+ my $record;
+ unless ($record = $args->{record}) {
+ return unless $args->{authid};
+ $record = GetAuthority($args->{authid});
+ }
+ return unless (ref $record eq 'MARC::Record');
+ if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
+# construct UNIMARC summary, that is quite different from MARC21 one
+# accepted form
+ foreach my $field ($record->field('2..')) {
+ return $field->as_string('abcdefghijlmnopqrstuvwxyz');
+ }
+ } else {
+ foreach my $field ($record->field('1..')) {
+ my $tag = $field->tag();
+ next if "152" eq $tag;
+# FIXME - 152 is not a good tag to use
+# in MARC21 -- purely local tags really ought to be
+# 9XX
+ if ($tag eq '100') {
+ return $field->as_string('abcdefghjklmnopqrstvxyz68');
+ } elsif ($tag eq '110') {
+ return $field->as_string('abcdefghklmnoprstvxyz68');
+ } elsif ($tag eq '111') {
+ return $field->as_string('acdefghklnpqstvxyz68');
+ } elsif ($tag eq '130') {
+ return $field->as_string('adfghklmnoprstvxyz68');
+ } elsif ($tag eq '148') {
+ return $field->as_string('abvxyz68');
+ } elsif ($tag eq '150') {
+ return $field->as_string('abvxyz68');
+ } elsif ($tag eq '151') {
+ return $field->as_string('avxyz68');
+ } elsif ($tag eq '155') {
+ return $field->as_string('abvxyz68');
+ } elsif ($tag eq '180') {
+ return $field->as_string('vxyz68');
+ } elsif ($tag eq '181') {
+ return $field->as_string('vxyz68');
+ } elsif ($tag eq '182') {
+ return $field->as_string('vxyz68');
+ } elsif ($tag eq '185') {
+ return $field->as_string('vxyz68');
+ } else {
+ return $field->as_string();
+ }
+ }
+ }
+ return;
}
-=head2 BuildUnimarcHierarchies
+=head2 BuildAuthHierarchies
- $text= &BuildUnimarcHierarchies( $authid, $force)
+ $text= &BuildAuthHierarchies( $authid, $force)
return text containing trees for hierarchies
for them to be stored in auth_header
=cut
-sub BuildUnimarcHierarchies{
- my $authid = shift @_;
+sub BuildAuthHierarchies{
+ my $authid = shift @_;
# warn "authid : $authid";
- my $force = shift @_;
- my @globalresult;
- my $dbh=C4::Context->dbh;
- my $hierarchies;
- my $data = GetHeaderAuthority($authid);
- if ($data->{'authtrees'} and not $force){
- return $data->{'authtrees'};
+ my $force = shift @_ || (C4::Context->preference('marcflavour') eq 'UNIMARC' ? 0 : 1);
+ my @globalresult;
+ my $dbh=C4::Context->dbh;
+ my $hierarchies;
+ my $data = GetHeaderAuthority($authid);
+ if ($data->{'authtrees'} and not $force){
+ return $data->{'authtrees'};
# } elsif ($data->{'authtrees'}){
# $hierarchies=$data->{'authtrees'};
- } else {
- my $record = GetAuthority($authid);
- my $found;
- return unless $record;
- foreach my $field ($record->field('5..')){
- if ($field->subfield('5') && $field->subfield('5') eq 'g'){
- my $subfauthid=_get_authid_subfield($field);
- next if ($subfauthid eq $authid);
- my $parentrecord = GetAuthority($subfauthid);
- my $localresult=$hierarchies;
- my $trees;
- $trees = BuildUnimarcHierarchies($subfauthid);
- my @trees;
- if ($trees=~/;/){
- @trees = split(/;/,$trees);
- } else {
- push @trees, $trees;
- }
- foreach (@trees){
- $_.= ",$authid";
+ } else {
+ my $record = GetAuthority($authid);
+ my $found;
+ return unless $record;
+ foreach my $field ($record->field('5..')){
+ my $broader = 0;
+ $broader = 1 if (
+ (C4::Context->preference('marcflavour') eq 'UNIMARC' && $field->subfield('5') && $field->subfield('5') eq 'g') ||
+ (C4::Context->preference('marcflavour') ne 'UNIMARC' && $field->subfield('w') && substr($field->subfield('w'), 0, 1) eq 'g'));
+ if ($broader) {
+ my $subfauthid=_get_authid_subfield($field) || '';
+ next if ($subfauthid eq $authid);
+ my $parentrecord = GetAuthority($subfauthid);
+ next unless $parentrecord;
+ my $localresult=$hierarchies;
+ my $trees;
+ $trees = BuildAuthHierarchies($subfauthid);
+ my @trees;
+ if ($trees=~/;/){
+ @trees = split(/;/,$trees);
+ } else {
+ push @trees, $trees;
+ }
+ foreach (@trees){
+ $_.= ",$authid";
+ }
+ @globalresult = (@globalresult,@trees);
+ $found=1;
+ }
+ $hierarchies=join(";",@globalresult);
}
- @globalresult = (@globalresult,@trees);
- $found=1;
- }
- $hierarchies=join(";",@globalresult);
+#Unless there is no ancestor, I am alone.
+ $hierarchies="$authid" unless ($hierarchies);
}
- #Unless there is no ancestor, I am alone.
- $hierarchies="$authid" unless ($hierarchies);
- }
- AddAuthorityTrees($authid,$hierarchies);
- return $hierarchies;
+ AddAuthorityTrees($authid,$hierarchies);
+ return $hierarchies;
}
-=head2 BuildUnimarcHierarchy
+=head2 BuildAuthHierarchy
- $ref= &BuildUnimarcHierarchy( $record, $class,$authid)
+ $ref= &BuildAuthHierarchy( $record, $class,$authid)
return a hashref in order to display hierarchy for record and final Authid $authid
"current_value"
"value"
-"ifparents"
-"ifchildren"
-Those two latest ones should disappear soon.
+=cut
+
+sub BuildAuthHierarchy{
+ my $record = shift @_;
+ my $class = shift @_;
+ my $authid_constructed = shift @_;
+ return unless ($record && $record->field('001'));
+ my $authid=$record->field('001')->data();
+ my %cell;
+ my $parents=""; my $children="";
+ my (@loopparents,@loopchildren);
+ my $marcflavour = C4::Context->preference('marcflavour');
+ my $relationshipsf = $marcflavour eq 'UNIMARC' ? '5' : 'w';
+ foreach my $field ($record->field('5..')){
+ my $subfauthid=_get_authid_subfield($field);
+ if ($subfauthid && $field->subfield($relationshipsf) && $field->subfield('a')){
+ my $relationship = substr($field->subfield($relationshipsf), 0, 1);
+ if ($relationship eq 'h'){
+ push @loopchildren, { "authid"=>$subfauthid,"value"=>$field->subfield('a')};
+ }
+ elsif ($relationship eq 'g'){
+ push @loopparents, { "authid"=>$subfauthid,"value"=>$field->subfield('a')};
+ }
+# brothers could get in there with an else
+ }
+ }
+ $cell{"parents"}=\@loopparents;
+ $cell{"children"}=\@loopchildren;
+ $cell{"class"}=$class;
+ $cell{"authid"}=$authid;
+ $cell{"current_value"} =1 if ($authid eq $authid_constructed);
+ $cell{"value"}=C4::Context->preference('marcflavour') eq 'UNIMARC' ? $record->subfield('2..',"a") : $record->subfield('1..', 'a');
+ return \%cell;
+}
+
+=head2 BuildAuthHierarchyBranch
+
+ $branch = &BuildAuthHierarchyBranch( $tree, $authid[, $cnt])
+
+Return a data structure representing an authority hierarchy
+given a list of authorities representing a single branch in
+an authority hierarchy tree. $authid is the current node in
+the tree (which may or may not be somewhere in the middle).
+$cnt represents the level of the upper-most item, and is only
+used when BuildAuthHierarchyBranch is called recursively (i.e.,
+don't ever pass in anything but zero to it).
=cut
-sub BuildUnimarcHierarchy{
- my $record = shift @_;
- my $class = shift @_;
- my $authid_constructed = shift @_;
- return undef unless ($record);
- my $authid=$record->field('001')->data();
- my %cell;
- my $parents=""; my $children="";
- my (@loopparents,@loopchildren);
- foreach my $field ($record->field('5..')){
- my $subfauthid=_get_authid_subfield($field);
- if ($subfauthid && $field->subfield('5') && $field->subfield('a')){
- if ($field->subfield('5') eq 'h'){
- push @loopchildren, { "childauthid"=>$field->subfield('3'),"childvalue"=>$field->subfield('a')};
- }
- elsif ($field->subfield('5') eq 'g'){
- push @loopparents, { "parentauthid"=>$field->subfield('3'),"parentvalue"=>$field->subfield('a')};
- }
- # brothers could get in there with an else
- }
- }
- $cell{"ifparents"}=1 if (scalar(@loopparents)>0);
- $cell{"ifchildren"}=1 if (scalar(@loopchildren)>0);
- $cell{"loopparents"}=\@loopparents if (scalar(@loopparents)>0);
- $cell{"loopchildren"}=\@loopchildren if (scalar(@loopchildren)>0);
- $cell{"class"}=$class;
- $cell{"loopauthid"}=$authid;
- $cell{"current_value"} =1 if $authid eq $authid_constructed;
- $cell{"value"}=$record->subfield('2..',"a");
- return \%cell;
+sub BuildAuthHierarchyBranch {
+ my ($tree, $authid, $cnt) = @_;
+ $cnt |= 0;
+ my $elementdata = GetAuthority(shift @$tree);
+ my $branch = BuildAuthHierarchy($elementdata,"child".$cnt, $authid);
+ if (scalar @$tree > 0) {
+ my $nextBranch = BuildAuthHierarchyBranch($tree, $authid, ++$cnt);
+ my $nextAuthid = $nextBranch->{authid};
+ my $found;
+ # If we already have the next branch listed as a child, let's
+ # replace the old listing with the new one. If not, we will add
+ # the branch at the end.
+ foreach my $cell (@{$branch->{children}}) {
+ if ($cell->{authid} eq $nextAuthid) {
+ $cell = $nextBranch;
+ $found = 1;
+ last;
+ }
+ }
+ push @{$branch->{children}}, $nextBranch unless $found;
+ }
+ return $branch;
+}
+
+=head2 GenerateHierarchy
+
+ $hierarchy = &GenerateHierarchy($authid);
+
+Return an arrayref holding one or more "trees" representing
+authority hierarchies.
+
+=cut
+
+sub GenerateHierarchy {
+ my ($authid) = @_;
+ my $trees = BuildAuthHierarchies($authid);
+ my @trees = split /;/,$trees ;
+ push @trees,$trees unless (@trees);
+ my @loophierarchies;
+ foreach my $tree (@trees){
+ my @tree=split /,/,$tree;
+ push @tree, $tree unless (@tree);
+ my $branch = BuildAuthHierarchyBranch(\@tree, $authid);
+ push @loophierarchies, [ $branch ];
+ }
+ return \@loophierarchies;
}
sub _get_authid_subfield{
my ($field)=@_;
return $field->subfield('9')||$field->subfield('3');
}
+
=head2 GetHeaderAuthority
$ref= &GetHeaderAuthority( $authid)
=head2 merge
- $ref= &merge(mergefrom,$MARCfrom,$mergeto,$MARCto)
+ $count = merge({
+ mergefrom => $mergefrom,
+ [ MARCfrom => $MARCfrom, ]
+ [ mergeto => $mergeto, ]
+ [ MARCto => $MARCto, ]
+ [ biblionumbers => [ $a, $b, $c ], ]
+ [ override_limit => 1, ]
+ });
+
+Merge biblios linked to authority $mergefrom (mandatory parameter).
+If $mergeto equals mergefrom, the linked biblio field is updated.
+If $mergeto is different, the biblio field will be linked to $mergeto.
+If $mergeto is missing, the biblio field is deleted.
+
+MARCfrom is used to determine if a cleared subfield in the authority record
+should be removed from a biblio. MARCto is used to populate the biblio
+record with the updated values; if you do not pass it, the biblio field
+will be deleted (same as missing mergeto).
+
+Normally all biblio records linked to $mergefrom, will be considered. But
+you can pass specific numbers via the biblionumbers parameter.
-Could add some feature : Migrating from a typecode to an other for instance.
-Then we should add some new parameter : bibliotargettag, authtargettag
+The parameter override_limit is used by the cron job to force larger
+postponed merges.
+
+Note: Although $mergefrom and $mergeto will normally be of the same
+authority type, merge also supports moving to another authority type.
=cut
sub merge {
- my ($mergefrom,$MARCfrom,$mergeto,$MARCto) = @_;
- my ($counteditedbiblio,$countunmodifiedbiblio,$counterrors)=(0,0,0);
- my $dbh=C4::Context->dbh;
- my $authtypecodefrom = GetAuthTypeCode($mergefrom);
- my $authtypecodeto = GetAuthTypeCode($mergeto);
-# warn "mergefrom : $authtypecodefrom $mergefrom mergeto : $authtypecodeto $mergeto ";
- # return if authority does not exist
- return "error MARCFROM not a marcrecord ".Data::Dumper::Dumper($MARCfrom) if scalar($MARCfrom->fields()) == 0;
- return "error MARCTO not a marcrecord".Data::Dumper::Dumper($MARCto) if scalar($MARCto->fields()) == 0;
- # search the tag to report
- my $sth = $dbh->prepare("select auth_tag_to_report from auth_types where authtypecode=?");
- $sth->execute($authtypecodefrom);
- my ($auth_tag_to_report_from) = $sth->fetchrow;
- $sth->execute($authtypecodeto);
- my ($auth_tag_to_report_to) = $sth->fetchrow;
-
+ my ( $params ) = @_;
+ my $mergefrom = $params->{mergefrom} || return;
+ my $MARCfrom = $params->{MARCfrom};
+ my $mergeto = $params->{mergeto};
+ my $MARCto = $params->{MARCto};
+ my $override_limit = $params->{override_limit};
+
+ # If we do not have biblionumbers, we get all linked biblios if the
+ # number of linked records does not exceed the limit UNLESS we override.
+ my @biblionumbers;
+ if( $params->{biblionumbers} ) {
+ @biblionumbers = @{ $params->{biblionumbers} };
+ } elsif( $override_limit ) {
+ @biblionumbers = Koha::Authorities->linked_biblionumbers({ authid => $mergefrom });
+ } else { # now first check number of linked records
+ my $max = C4::Context->preference('AuthorityMergeLimit') // 0;
+ my $hits = Koha::Authorities->get_usage_count({ authid => $mergefrom });
+ if( $hits > 0 && $hits <= $max ) {
+ @biblionumbers = Koha::Authorities->linked_biblionumbers({ authid => $mergefrom });
+ } elsif( $hits > $max ) { #postpone this merge to the cron job
+ Koha::Authority::MergeRequest->new({
+ authid => $mergefrom,
+ oldrecord => $MARCfrom,
+ authid_new => $mergeto,
+ })->store;
+ }
+ }
+ return 0 if !@biblionumbers;
+
+ # Search authtypes and reporting tags
+ my $authfrom = Koha::Authorities->find($mergefrom);
+ my $authto = Koha::Authorities->find($mergeto);
+ my $authtypefrom = $authfrom ? Koha::Authority::Types->find($authfrom->authtypecode) : undef;
+ my $authtypeto = $authto ? Koha::Authority::Types->find($authto->authtypecode) : undef;
+ my $auth_tag_to_report_from = $authtypefrom ? $authtypefrom->auth_tag_to_report : '';
+ my $auth_tag_to_report_to = $authtypeto ? $authtypeto->auth_tag_to_report : '';
+
my @record_to;
- @record_to = $MARCto->field($auth_tag_to_report_to)->subfields() if $MARCto->field($auth_tag_to_report_to);
+ @record_to = $MARCto->field($auth_tag_to_report_to)->subfields() if $auth_tag_to_report_to && $MARCto && $MARCto->field($auth_tag_to_report_to);
my @record_from;
- @record_from = $MARCfrom->field($auth_tag_to_report_from)->subfields() if $MARCfrom->field($auth_tag_to_report_from);
-
- my @reccache;
- # search all biblio tags using this authority.
- #Getting marcbiblios impacted by the change.
- if (C4::Context->preference('NoZebra')) {
- #nozebra way
- my $dbh=C4::Context->dbh;
- my $rq=$dbh->prepare(qq(SELECT biblionumbers from nozebra where indexname="an" and server="biblioserver" and value="$mergefrom" ));
- $rq->execute;
- while (my $biblionumbers=$rq->fetchrow){
- my @biblionumbers=split /;/,$biblionumbers;
- foreach (@biblionumbers) {
- if ($_=~/(\d+),.*/) {
- my $marc=GetMarcBiblio($1);
- push @reccache,$marc;
- }
- }
- }
- } else {
- #zebra connection
- my $oConnection=C4::Context->Zconn("biblioserver",0);
- my $oldSyntax = $oConnection->option("preferredRecordSyntax");
- $oConnection->option("preferredRecordSyntax"=>"XML");
- my $query;
- $query= "an=".$mergefrom;
- my $oResult = $oConnection->search(new ZOOM::Query::CCL2RPN( $query, $oConnection ));
- my $count = 0;
- if ($oResult) {
- $count=$oResult->size();
- }
- my $z=0;
- while ( $z<$count ) {
- my $rec;
- $rec=$oResult->record($z);
- my $marcdata = $rec->raw();
- push @reccache, $marcdata;
- $z++;
- }
- $oResult->destroy();
- $oConnection->option("preferredRecordSyntax"=>$oldSyntax);
+ if( !$authfrom && $MARCfrom && $MARCfrom->field('1..','2..') ) {
+ # postponed merge, authfrom was deleted and MARCfrom only contains the old reporting tag (and possibly a 100 for UNIMARC)
+ # 2XX is for UNIMARC; we use -1 in order to skip 100 in UNIMARC; this will not impact MARC21, since there is only one tag
+ @record_from = ( $MARCfrom->field('1..','2..') )[-1]->subfields;
+ } elsif( $auth_tag_to_report_from && $MARCfrom && $MARCfrom->field($auth_tag_to_report_from) ) {
+ @record_from = $MARCfrom->field($auth_tag_to_report_from)->subfields;
}
- #warn scalar(@reccache)." biblios to update";
- # Get All candidate Tags for the change
+
+ # Get all candidate tags for the change
# (This will reduce the search scope in marc records).
- $sth = $dbh->prepare("select distinct tagfield from marc_subfield_structure where authtypecode=?");
- $sth->execute($authtypecodefrom);
- my @tags_using_authtype;
- while (my ($tagfield) = $sth->fetchrow) {
- push @tags_using_authtype,$tagfield ;
- }
- my $tag_to=0;
- if ($authtypecodeto ne $authtypecodefrom){
- # If many tags, take the first
- $sth->execute($authtypecodeto);
- $tag_to=$sth->fetchrow;
- #warn $tag_to;
+ # For a deleted authority record, we scan all auth controlled fields
+ my $dbh = C4::Context->dbh;
+ my $sql = "SELECT DISTINCT tagfield FROM marc_subfield_structure WHERE authtypecode=?";
+ my $tags_using_authtype = $authtypefrom ? $dbh->selectcol_arrayref( $sql, undef, ( $authtypefrom->authtypecode )) : $dbh->selectcol_arrayref( "SELECT DISTINCT tagfield FROM marc_subfield_structure WHERE authtypecode IS NOT NULL AND authtypecode<>''" );
+ my $tags_new;
+ if( $authtypeto && ( !$authtypefrom || $authtypeto->authtypecode ne $authtypefrom->authtypecode )) {
+ $tags_new = $dbh->selectcol_arrayref( $sql, undef, ( $authtypeto->authtypecode ));
}
- # BulkEdit marc records
- # May be used as a template for a bulkedit field
- foreach my $marcrecord(@reccache){
- my $update;
- $marcrecord= MARC::Record->new_from_xml($marcrecord,"utf8",C4::Context->preference("marcflavour")) unless(C4::Context->preference('NoZebra'));
- foreach my $tagfield (@tags_using_authtype){
-# warn "tagfield : $tagfield ";
- foreach my $field ($marcrecord->field($tagfield)){
- my $auth_number=$field->subfield("9");
- my $tag=$field->tag();
- if ($auth_number==$mergefrom) {
- my $field_to=MARC::Field->new(($tag_to?$tag_to:$tag),$field->indicator(1),$field->indicator(2),"9"=>$mergeto);
- my $exclude='9';
- foreach my $subfield (@record_to) {
- $field_to->add_subfields($subfield->[0] =>$subfield->[1]);
- $exclude.= $subfield->[0];
+
+ my $overwrite = C4::Context->preference( 'AuthorityMergeMode' ) eq 'strict';
+ my $skip_subfields = $overwrite
+ # This hash contains all subfields from the authority report fields
+ # Including $MARCfrom as well as $MARCto
+ # We only need it in loose merge mode; replaces the former $exclude
+ ? {}
+ : { map { ( $_->[0], 1 ); } ( @record_from, @record_to ) };
+ # And we need to add $9 in order not to duplicate
+ $skip_subfields->{9} = 1 if !$overwrite;
+
+ my $counteditedbiblio = 0;
+ foreach my $biblionumber ( @biblionumbers ) {
+ my $marcrecord = GetMarcBiblio({ biblionumber => $biblionumber });
+ next if !$marcrecord;
+ my $update = 0;
+ foreach my $tagfield (@$tags_using_authtype) {
+ my $countfrom = 0; # used in strict mode to remove duplicates
+ foreach my $field ( $marcrecord->field($tagfield) ) {
+ my $auth_number = $field->subfield("9"); # link to authority
+ my $tag = $field->tag();
+ next if !defined($auth_number) || $auth_number ne $mergefrom;
+ $countfrom++;
+ if ( !$mergeto || !@record_to ||
+ ( $overwrite && $countfrom > 1 ) ) {
+ # !mergeto or !record_to indicates a delete
+ # Other condition: remove this duplicate in strict mode
+ $marcrecord->delete_field($field);
+ $update = 1;
+ next;
}
- $exclude='['.$exclude.']';
-# add subfields in $field not included in @record_to
- my @restore= grep {$_->[0]!~/$exclude/} $field->subfields();
- foreach my $subfield (@restore) {
- $field_to->add_subfields($subfield->[0] =>$subfield->[1]);
- }
- $marcrecord->delete_field($field);
- $marcrecord->insert_grouped_field($field_to);
- $update=1;
+ my $newtag = $tags_new
+ ? _merge_newtag( $tag, $tags_new )
+ : $tag;
+ my $field_to = MARC::Field->new(
+ $newtag,
+ $field->indicator(1),
+ $field->indicator(2),
+ "9" => $mergeto,
+ );
+ foreach my $subfield ( grep { $_->[0] ne '9' } @record_to ) {
+ $field_to->add_subfields( $subfield->[0] => $subfield->[1] );
}
- }#for each tag
- }#foreach tagfield
- my ($bibliotag,$bibliosubf) = GetMarcFromKohaField("biblio.biblionumber","") ;
- my $biblionumber;
- if ($bibliotag<10){
- $biblionumber=$marcrecord->field($bibliotag)->data;
- }
- else {
- $biblionumber=$marcrecord->subfield($bibliotag,$bibliosubf);
- }
- unless ($biblionumber){
- warn "pas de numéro de notice bibliographique dans : ".$marcrecord->as_formatted;
- next;
+ if ( !$overwrite ) {
+ # add subfields back in loose mode, check skip_subfields
+ foreach my $subfield ( $field->subfields ) {
+ next if $skip_subfields->{ $subfield->[0] };
+ $field_to->add_subfields( $subfield->[0], $subfield->[1] );
+ }
+ }
+ if ($tags_new) {
+ $marcrecord->delete_field($field);
+ append_fields_ordered( $marcrecord, $field_to );
+ } else {
+ $field->replace_with($field_to);
+ }
+ $update = 1;
+ }
}
- if ($update==1){
- &ModBiblio($marcrecord,$biblionumber,GetFrameworkCode($biblionumber)) ;
- $counteditedbiblio++;
- warn $counteditedbiblio if (($counteditedbiblio % 10) and $ENV{DEBUG});
- }
- }#foreach $marc
- return $counteditedbiblio;
- # now, find every other authority linked with this authority
- # now, find every other authority linked with this authority
-# my $oConnection=C4::Context->Zconn("authorityserver");
-# my $query;
-# # att 9210 Auth-Internal-authtype
-# # att 9220 Auth-Internal-LN
-# # ccl.properties to add for authorities
-# $query= "= ".$mergefrom;
-# my $oResult = $oConnection->search(new ZOOM::Query::CCL2RPN( $query, $oConnection ));
-# my $count=$oResult->size() if ($oResult);
-# my @reccache;
-# my $z=0;
-# while ( $z<$count ) {
-# my $rec;
-# $rec=$oResult->record($z);
-# my $marcdata = $rec->raw();
-# push @reccache, $marcdata;
-# $z++;
-# }
-# $oResult->destroy();
-# foreach my $marc(@reccache){
-# my $update;
-# my $marcrecord;
-# $marcrecord = MARC::File::USMARC::decode($marc);
-# foreach my $tagfield (@tags_using_authtype){
-# $tagfield=substr($tagfield,0,3);
-# my @tags = $marcrecord->field($tagfield);
-# foreach my $tag (@tags){
-# my $tagsubs=$tag->subfield("9");
-# #warn "$tagfield:$tagsubs:$mergefrom";
-# if ($tagsubs== $mergefrom) {
-# $tag->update("9" =>$mergeto);
-# foreach my $subfield (@record_to) {
-# # warn "$subfield,$subfield->[0],$subfield->[1]";
-# $tag->update($subfield->[0] =>$subfield->[1]);
-# }#for $subfield
-# }
-# $marcrecord->delete_field($tag);
-# $marcrecord->add_fields($tag);
-# $update=1;
-# }#for each tag
-# }#foreach tagfield
-# my $authoritynumber = TransformMarcToKoha($dbh,$marcrecord,"") ;
-# if ($update==1){
-# &ModAuthority($marcrecord,$authoritynumber,GetAuthTypeCode($authoritynumber)) ;
-# }
-#
-# }#foreach $marc
-}#sub
+ next if !$update;
+ ModBiblio($marcrecord, $biblionumber, GetFrameworkCode($biblionumber));
+ $counteditedbiblio++;
+ }
+ return $counteditedbiblio;
+}
+
+sub _merge_newtag {
+# Routine is only called for an (exceptional) authtypecode change
+# Fixes old behavior of returning the first tag found
+ my ( $oldtag, $new_tags ) = @_;
+
+ # If we e.g. have 650 and 151,651,751 try 651 and check presence
+ my $prefix = substr( $oldtag, 0, 1 );
+ my $guess = $prefix . substr( $new_tags->[0], -2 );
+ if( grep { $_ eq $guess } @$new_tags ) {
+ return $guess;
+ }
+ # Otherwise return one from the same block e.g. 6XX for 650
+ # If not there too, fall back to first new tag (old behavior!)
+ my @same_block = grep { /^$prefix/ } @$new_tags;
+ return @same_block ? $same_block[0] : $new_tags->[0];
+}
+
+sub append_fields_ordered {
+# while we lack this function in MARC::Record
+# we do not want insert_fields_ordered since it inserts before
+ my ( $record, $field ) = @_;
+ if( my @flds = $record->field( $field->tag ) ) {
+ $record->insert_fields_after( pop @flds, $field );
+ } else { # now fallback to insert_fields_ordered
+ $record->insert_fields_ordered( $field );
+ }
+}
=head2 get_auth_type_location