X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FAuthoritiesMarc.pm;h=7443f36c15da57b8460c259e1e7a28ebf2e2014f;hb=08382876306cfda839637c5f72a107b304458a8e;hp=0122ca88b31b9c360ef7865835460b76ffebacf7;hpb=88b3711ca918877796d8577c54efe3f0aba96673;p=koha.git diff --git a/C4/AuthoritiesMarc.pm b/C4/AuthoritiesMarc.pm index 0122ca88b3..7443f36c15 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; @@ -27,12 +26,13 @@ use C4::AuthoritiesMarc::MARC21; use C4::AuthoritiesMarc::UNIMARC; use C4::Charset; use C4::Log; +use Koha::Authority; use vars qw($VERSION @ISA @EXPORT); BEGIN { # set the version for version checking - $VERSION = 3.01; + $VERSION = 3.07.00.049; require Exporter; @ISA = qw(Exporter); @@ -41,21 +41,21 @@ BEGIN { &GetAuthType &GetAuthTypeCode &GetAuthMARCFromKohaField - &AUTHhtml2marc &AddAuthority &ModAuthority &DelAuthority &GetAuthority &GetAuthorityXML - + &CountUsage &CountUsageChildren &SearchAuthorities - &BuildSummary - &BuildUnimarcHierarchies - &BuildUnimarcHierarchy + &BuildSummary + &BuildAuthHierarchies + &BuildAuthHierarchy + &GenerateHierarchy &merge &FindDuplicateAuthority @@ -98,253 +98,224 @@ 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')) { - - # - # 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 + 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 mainentry search $a tag - if (@$tags[$i] eq "mainmainentry") { - -# FIXME: 'Heading-Main' index not yet defined in zebra -# $attr =" \@attr 1=Heading-Main "; - $attr =" \@attr 1=Heading "; - - }elsif (@$tags[$i] eq "mainentry") { - $attr =" \@attr 1=Heading "; - }else{ - $attr =" \@attr 1=Any "; - } - 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 - } - @$value[$i] =~ s/"/\\"/g; # Escape the double-quotes in the search value - $attr =$attr."\"".@$value[$i]."\""; - $q2 .=$attr; - $dosearch=1; - ++$attr_cnt; - }#if value + if ($QParser) { + $qpquery .= '(authtype:' . join('|| authtype:', @auths) . ')'; } - ##Add how many queries generated - if ($query=~/\S+/){ - $query= $and x $attr_cnt . $query . $q2; - } else { - $query= $q2; + } + + my $dosearch; + my $and=" \@and " ; + my $q2; + my $attr_cnt = 0; + 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 "; + } + 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 "; + } + else { # Assume any if no index was specified + $attr = " \@attr 1=Any "; + } + 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 + 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 ($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"; + + $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 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){ + + ##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 $summary=BuildSummary($authrecord,$authid,$authtypecode); - my $query_auth_tag = "SELECT auth_tag_to_report FROM auth_types WHERE authtypecode=?"; + ##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]; + + foreach ( $mainentry->subfields() ) { + $reported_tag .= '$' . $_->[0] . $_->[1]; } } - my %newline; - $newline{summary} = $summary; - $newline{authid} = $authid; - $newline{even} = $counter % 2; + my $thisauthtype = GetAuthType(GetAuthTypeCode($authid)); + unless (defined $thisauthtype) { + $thisauthtype = GetAuthType($authtypecode) if $authtypecode; + } + $newline{authtype} = defined($thisauthtype) ? + $thisauthtype->{'authtypetext'} : ''; + $newline{summary} = $summary; + $newline{even} = $counter % 2; $newline{reported_tag} = $reported_tag; - $counter++; - push @finalresult, \%newline; - }## while counter + } + $counter++; + push @finalresult, \%newline; + }## while counter ### - for (my $z=0; $z<@finalresult; $z++){ + 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); - } + }# all $z's + } + + }## if nbresult + NOLUCK: + $oAResult->destroy(); + # $oAuth[0]->destroy(); + + return (\@finalresult, $nbresults); } =head2 CountUsage @@ -357,18 +328,16 @@ counts Usage of Authid in bibliorecords. 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; + $query= "an:".$authid; my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10); - return ($result); - } + if ($err) { + warn "Error: $err from search $query"; + $result = 0; + } + + return $result; } =head2 CountUsageChildren @@ -410,9 +379,9 @@ Get the record and tries to guess the adequate authtypecode from its content. =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'}, @@ -551,7 +520,7 @@ sub GetTagsLabels { $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" @@ -566,12 +535,13 @@ 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 ) { @@ -587,6 +557,7 @@ ORDER BY tagfield,tagsubfield" $res->{$tag}->{$subfield}->{hidden} = $hidden; $res->{$tag}->{$subfield}->{isurl} = $isurl; $res->{$tag}->{$subfield}->{link} = $link; + $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue; } return $res; } @@ -637,9 +608,16 @@ sub AddAuthority { } my $date=POSIX::strftime("%y%m%d",localtime); if (!$record->field('008')) { - $record->insert_fields_ordered( - MARC::Field->new('008',$date."|||a|||||| | ||| d") - ); + # Get a valid default value for field 008 + my $default_008 = C4::Context->preference('MARCAuthorityControlField008'); + if(!$default_008 or length($default_008)<34) { + $default_008 = '|| aca||aabn | a|a d'; + } + else { + $default_008 = substr($default_008,0,34); + } + + $record->insert_fields_ordered( MARC::Field->new('008',$date.$default_008) ); } if (!$record->field('040')) { $record->insert_fields_ordered( @@ -651,17 +629,22 @@ sub AddAuthority { } } - if (($format eq "UNIMARCAUTH") && (!$record->subfield('100','a'))){ + if ($format eq "UNIMARCAUTH") { $record->leader(" nx j22 ") unless ($record->leader()); - my $date=POSIX::strftime("%Y%m%d",localtime); - if ($record->field('100')){ - $record->field('100')->update('a'=>$date."afrey50 ba0"); - } else { - $record->append_fields( - MARC::Field->new('100',' ',' ' - ,'a'=>$date."afrey50 ba0") - ); - } + 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.$defaultfield100); + } else { + $record->append_fields( + MARC::Field->new('100',' ',' ' + ,'a'=>$date.$defaultfield100) + ); + } } my ($auth_type_tag, $auth_type_subfield) = get_auth_type_location($authtypecode); if (!$authid and $format eq "MARC21") { @@ -728,30 +711,33 @@ sub DelAuthority { $sth->execute($authid); } +=head2 ModAuthority + + $authid= &ModAuthority($authid,$record,$authtypecode) + +Modifies authority record, optionally updates attached biblios. + +=cut + sub ModAuthority { - my ($authid,$record,$authtypecode,$merge)=@_; + 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 to use merge_authotities.p -### they should have a system preference "dontmerge=1" otherwise by default biblios will be updated -### the $merge flag is now depreceated and will be removed at code cleaning - if (C4::Context->preference('MergeAuthoritiesOnUpdate') ){ + # 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 the file in tmp/modified_authorities - my $cgidir = C4::Context->intranetdir ."/cgi-bin"; - unless (opendir(DIR,"$cgidir")) { - $cgidir = C4::Context->intranetdir."/"; - closedir(DIR); - } - - my $filename = $cgidir."/tmp/modified_authorities/$authid.authid"; - open AUTH, "> $filename"; - print AUTH $authid; - close AUTH; + # 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; @@ -797,19 +783,9 @@ Returns MARC::Record of the authority passed in parameter. 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); + my $authority = Koha::Authority->get_from_authid($authid); + return unless $authority; + return ($authority->record); } =head2 GetAuthType @@ -838,53 +814,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) @@ -907,16 +836,24 @@ sub FindDuplicateAuthority { $sth->finish; # warn "record :".$record->as_formatted." auth_tag_to_report :$auth_tag_to_report"; # build a request for SearchAuthorities - my $query='at='.$authtypecode.' '; + 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\!\'\"\`\#\$\%\&\*\+,\-\./:;<=>\?\@\(\)\{\[\]\}_\|\~]); 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]/); + $_->[1]=~s/$filtervalues/ /g; $query.= " $op he:\"".$_->[1]."\"" if ($_->[0]=~/[A-z]/); } } my ($error, $results, $total_hits) = C4::Search::SimpleSearch( $query, 0, 1, [ "authorityserver" ] ); # there is at least 1 result => return the 1st one - if (@$results>0) { + if (!defined $error && @{$results} ) { my $marcrecord = MARC::File::USMARC::decode($results->[0]); return $marcrecord->field('001')->data,BuildSummary($marcrecord,$marcrecord->field('001')->data,$authtypecode); } @@ -926,9 +863,9 @@ sub FindDuplicateAuthority { =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. Moreover, authid can also be inferred from $record. @@ -936,176 +873,330 @@ 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; + # handle $authtypecode is NULL or eq "" + if ($authtypecode) { + my $authref = GetAuthType($authtypecode); + $summary{authtypecode} = $authref->{authtypecode}; + $summary{type} = $authref->{authtypetext}; + $summary{summary} = $authref->{summary}; + } + 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 @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{summary}; + $localsummary =~ s/\[(.?.?.?.?)$tag\*(.*?)\]/$1$tagvalue$2\[$1$tag$2\]/g; + if ($tag<10) { + if ($tag eq '001') { + $reported_tag.='$3'.$field->data(); + } + } 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; + } + } + push @stringssummary, $localsummary if ($localsummary ne $summary{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 $resultstring; + $resultstring = join(" -- ",@stringssummary); + $resultstring =~ s/\[(.*?)\]//g; + $resultstring =~ s/\n/
/g; + $summary{summary} = $resultstring; + } + 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..')) { + push @authorized, { + heading => $field->as_string('abcdefghijlmnopqrstuvwxyz'), + hemain => $field->subfield('a'), + field => $field->tag(), + }; + } +# rejected form(s) + foreach my $field ($record->field('3..')) { + push @notes, { note => $field->subfield('a'), field => $field->tag() }; + } + 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'), + 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'), + search => $heading, + authid => $_->subfield('9'), + } + } $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) ), + field => $tag, + }; + } else { + push @authorized, { + heading => $field->as_string(), + hemain => $field->subfield('a'), + 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 }; } - } - push @stringssummary, $localsummary if ($localsummary ne $summary); } - my $resultstring; - $resultstring = join(" -- ",@stringssummary); - $resultstring =~ s/\[(.*?)\]//g; - $resultstring =~ s/\n/
/g; - $summary = $resultstring; - } else { - my $heading; - my $altheading; - my $seealso; - my $broaderterms; - my $narrowerterms; - my $see; - my $seeheading; - my $notes; - my @fields = $record->fields(); + $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..')) { - $heading.= $field->as_string('abcdefghijlmnopqrstuvwxyz'); - } - # rejected form(s) - foreach my $field ($record->field('3..')) { - $notes.= ''.$field->subfield('a')."\n"; - } - foreach my $field ($record->field('4..')) { - if ($field->subfield('2')) { - my $thesaurus = "thes. : ".$thesaurus{"$field->subfield('2')"}." : "; - $see.= ''.$thesaurus.$field->as_string('abcdefghijlmnopqrstuvwxyz')." -- \n"; +# construct UNIMARC summary, that is quite different from MARC21 one +# accepted form + foreach my $field ($record->field('2..')) { + return $field->as_string('abcdefghijlmnopqrstuvwxyz'); } - } - # see : - foreach my $field ($record->field('5..')) { - - if (($field->subfield('5')) && ($field->subfield('a')) && ($field->subfield('5') eq 'g')) { - $broaderterms.= ' '.$field->as_string('abcdefgjxyz')." -- \n"; - } elsif (($field->subfield('5')) && ($field->as_string) && ($field->subfield('5') eq 'h')){ - $narrowerterms.= ''.$field->as_string('abcdefgjxyz')." -- \n"; - } elsif ($field->subfield('a')) { - $seealso.= ''.$field->as_string('abcdefgxyz')." -- \n"; + } 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(); + } } - } - # // form - foreach my $field ($record->field('7..')) { - my $lang = substr($field->subfield('8'),3,3); - $seeheading.= ' En '.$language{$lang}.' : '.$field->subfield('a')."
\n"; - } - $broaderterms =~s/-- \n$//; - $narrowerterms =~s/-- \n$//; - $seealso =~s/-- \n$//; - $see =~s/-- \n$//; - $summary = "".$heading."
".($notes?"$notes
":""); - $summary.= '

TG : '.$broaderterms.'

' if ($broaderterms); - $summary.= '

TS : '.$narrowerterms.'

' if ($narrowerterms); - $summary.= '

TA : '.$seealso.'

' if ($seealso); - $summary.= '

EP : '.$see.'

' if ($see); - $summary.= '

'.$seeheading.'

' 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.= "
      used for/see from: ".$field->as_string(); - } #See Also - foreach my $field ($record->field('5..')) { - $altheading.= "
      see also: ".$field->as_string(); - } - $summary .= ": " if $summary; - $summary.=$heading.$seeheading.$altheading; - } - } - return $summary; + } + 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 @@ -1115,53 +1206,59 @@ Example of text: =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'}; - } elsif ($data->{'authtrees'}){ - $hierarchies=$data->{'authtrees'}; - } else { - my $record = GetAuthority($authid); - my $found; - if ($record){ - foreach my $field ($record->field('550')){ - if ($field->subfield('5') && $field->subfield('5') eq 'g'){ - my $parentrecord = GetAuthority($field->subfield('3')); - my $localresult=$hierarchies; - my $trees; - $trees = BuildUnimarcHierarchies($field->subfield('3')); - my @trees; - if ($trees=~/;/){ - @trees = split(/;/,$trees); - } else { - push @trees, $trees; - } - foreach (@trees){ - $_.= ",$authid"; - } - @globalresult = (@globalresult,@trees); - $found=1; - } - $hierarchies=join(";",@globalresult); - } - } - #Unless there is no ancestor, I am alone. - $hierarchies="$authid" unless ($hierarchies); - } - AddAuthorityTrees($authid,$hierarchies); - return $hierarchies; + 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..')){ + 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); + } +#Unless there is no ancestor, I am alone. + $hierarchies="$authid" unless ($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 @@ -1172,41 +1269,101 @@ return a hashref in order to display hierarchy for record and final Authid $auth "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 ($field->subfield('5') && $field->subfield('a')){ - if ($field->subfield('5') eq 'h'){ - push @loopchildren, { "childauthid"=>$subfauthid,"childvalue"=>$field->subfield('a')}; - }elsif ($field->subfield('5') eq 'g'){ - push @loopparents, { "parentauthid"=>$subfauthid,"parentvalue"=>$field->subfield('a')}; - } - # brothers could get in there with an else +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; } - } - $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; + 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{ @@ -1282,41 +1439,30 @@ sub merge { 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); - $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++; - } - $oConnection->destroy(); + #zebra connection + my $oConnection=C4::Context->Zconn("biblioserver",0); + # We used to use XML syntax here, but that no longer works. + # Thankfully, we don't need it. + 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(); + my $marcrecordzebra= MARC::Record->new_from_usmarc($marcdata); + my ( $biblionumbertagfield, $biblionumbertagsubfield ) = &GetMarcFromKohaField( "biblio.biblionumber", '' ); + my $i = ($biblionumbertagfield < 10) ? $marcrecordzebra->field($biblionumbertagfield)->data : $marcrecordzebra->subfield($biblionumbertagfield, $biblionumbertagsubfield); + my $marcrecorddb=GetMarcBiblio($i); + push @reccache, $marcrecorddb; + $z++; } + $oResult->destroy(); #warn scalar(@reccache)." biblios to update"; # Get All candidate Tags for the change # (This will reduce the search scope in marc records). @@ -1337,16 +1483,16 @@ sub merge { # 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)){ + # biblio is linked to authority with $9 subfield containing authid 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) { + foreach my $subfield (grep {$_->[0] ne '9'} @record_to) { $field_to->add_subfields($subfield->[0] =>$subfield->[1]); $exclude.= $subfield->[0]; } @@ -1451,7 +1597,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") {