@@ -, +, @@ reservoir the "Merge" link for two records. merge-related links in the Manage staged MARC batch screen. identical records (or at least two records that you don't mind merging). resulting record. saved from your catalog. to merge in a new window" link. going through the entire merging process) matches your expectations. your catalog that will match based on that rule. For MARC21, the following is a good choice for a rule: Matching rule code: AUTHPER Description: Personal name main entry Match threshold: 999 Record type: Authority record [Match point 1:] Search index: mainmainentry Score: 1000 Tag: 100 Subfields: a just created. resulting record (after going through the entire merging process) matches your expectations. --- Koha/Authority.pm | 60 ++++++- Koha/Util/MARC.pm | 69 ++++++++ authorities/merge.pl | 187 ++++++++++++++++++++ authorities/merge_ajax.pl | 27 +++ .../prog/en/includes/authorities_js.inc | 39 ++++ .../en/modules/authorities/authorities-home.tt | 3 + .../prog/en/modules/authorities/merge.tt | 143 +++++++++++++++ .../en/modules/authorities/searchresultlist.tt | 9 +- .../prog/en/modules/tools/manage-marc-import.tt | 20 ++- t/Koha_Util_MARC.t | 3 +- t/db_dependent/Koha_Authority.t | 26 +++ 11 files changed, 579 insertions(+), 7 deletions(-) create mode 100755 authorities/merge.pl create mode 100755 authorities/merge_ajax.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/authorities_js.inc create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/authorities/merge.tt --- a/Koha/Authority.pm +++ a/Koha/Authority.pm @@ -37,6 +37,7 @@ use C4::Context; use MARC::Record; use MARC::File::XML; use C4::Charset; +use Koha::Util::MARC; use base qw(Koha::Record); @@ -53,7 +54,12 @@ sub new { my $class = shift; my $record = shift; - my $self = $class->SUPER::new( { record => $record }); + my $self = $class->SUPER::new( + { + 'record' => $record, + 'schema' => lc C4::Context->preference("marcflavour") + } + ); bless $self, $class; return $self; @@ -84,6 +90,13 @@ sub get_from_authid { return if ($@); $record->encoding('UTF-8'); + # NOTE: GuessAuthTypeCode has no business in Koha::Authority, which is an + # object-oriented class. Eventually perhaps there will be utility + # classes in the Koha:: namespace, but there are not at the moment, + # so this shim seems like the best option all-around. + require C4::AuthoritiesMarc; + $authtypecode ||= C4::AuthoritiesMarc::GuessAuthTypeCode($record); + my $self = $class->SUPER::new( { authid => $authid, authtype => $authtypecode, schema => $marcflavour, @@ -93,4 +106,49 @@ sub get_from_authid { return $self; } +=head2 get_from_breeding + + my $auth = Koha::Authority->get_from_authid($authid); + +Create the Koha::Authority object associated with the provided authid. + +=cut +sub get_from_breeding { + my $class = shift; + my $import_record_id = shift; + my $marcflavour = lc C4::Context->preference("marcflavour"); + + my $dbh=C4::Context->dbh; + my $sth=$dbh->prepare("select marcxml from import_records where import_record_id=? and record_type='auth';"); + $sth->execute($import_record_id); + my $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 if ($@); + $record->encoding('UTF-8'); + + # NOTE: GuessAuthTypeCode has no business in Koha::Authority, which is an + # object-oriented class. Eventually perhaps there will be utility + # classes in the Koha:: namespace, but there are not at the moment, + # so this shim seems like the best option all-around. + require C4::AuthoritiesMarc; + my $authtypecode = C4::AuthoritiesMarc::GuessAuthTypeCode($record); + + my $self = $class->SUPER::new( { + schema => $marcflavour, + authtype => $authtypecode, + record => $record }); + + bless $self, $class; + return $self; +} + +sub authorized_heading { + my ($self) = @_; + if ($self->schema =~ m/marc/) { + return Koha::Util::MARC::getAuthorityAuthorizedHeading($self->record, $self->schema); + } + return; +} + 1; --- a/Koha/Util/MARC.pm +++ a/Koha/Util/MARC.pm @@ -107,4 +107,73 @@ sub _createKey { return int(rand(1000000)); } +=head2 getAuthorityAuthorizedHeading + +Retrieve the authorized heading from a MARC authority record + +=cut + +sub getAuthorityAuthorizedHeading { + my ( $record, $schema ) = @_; + return unless ( ref $record eq 'MARC::Record' ); + if ( $schema 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; +} + 1; --- a/authorities/merge.pl +++ a/authorities/merge.pl @@ -0,0 +1,187 @@ +#!/usr/bin/perl + +# Copyright 2013 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use CGI; +use C4::Output; +use C4::Auth; +use C4::AuthoritiesMarc; +use Koha::Authority; +use C4::Koha; +use Koha::Record; +use C4::Biblio; + +my $input = new CGI; +my @authid = $input->param('authid'); +my $merge = $input->param('merge'); + +my @errors; + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "authorities/merge.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { editauthorities => 1 }, + } +); + +#------------------------ +# Merging +#------------------------ +if ($merge) { + + # Creating a new record from the html code + my $record = TransformHtmlToMarc($input); + my $recordid1 = $input->param('recordid1'); + my $recordid2 = $input->param('recordid2'); + my $typecode = $input->param('frameworkcode'); + + # Rewriting the leader + $record->leader( GetAuthority($recordid1)->leader() ); + + # Modifying the reference record + ModAuthority( $recordid1, $record, $typecode ); + + # Deleting the other record + if ( scalar(@errors) == 0 ) { + + my $error; + if ($input->param('mergereference') eq 'breeding') { + require C4::ImportBatch; + C4::ImportBatch::SetImportRecordStatus( $recordid2, 'imported' ); + } else { + $error = DelAuthority($recordid2); + } + push @errors, $error if ($error); + } + + # Parameters + $template->param( + result => 1, + recordid1 => $recordid1 + ); + + #------------------------- + # Show records to merge + #------------------------- +} +else { + my $mergereference = $input->param('mergereference'); + $template->{'VARS'}->{'mergereference'} = $mergereference; + + if ( scalar(@authid) != 2 ) { + push @errors, { code => "WRONG_COUNT", value => scalar(@authid) }; + } + else { + my $recordObj1 = Koha::Authority->get_from_authid($authid[0]) || Koha::Authority->new(); + my $recordObj2; + + if ($mergereference eq 'breeding') { + $recordObj2 = Koha::Authority->get_from_breeding($authid[1]) || Koha::Authority->new(); + $mergereference = $authid[0]; + } else { + $recordObj2 = Koha::Authority->get_from_authid($authid[1]) || Koha::Authority->new(); + } + + if ($mergereference) { + + my $framework; + if ( $recordObj1->authtype ne $recordObj2->authtype && $mergereference ne 'breeding' ) { + $framework = $input->param('frameworkcode') + or push @errors, "Framework not selected."; + } + else { + $framework = $recordObj1->authtype; + } + + # Getting MARC Structure + my $tagslib = GetTagsLabels( 1, $framework ); + + my $notreference = + ( $authid[0] == $mergereference ) + ? $authid[1] + : $authid[0]; + + # Creating a loop for display + + my @record1 = $recordObj1->createMergeHash($tagslib); + my @record2 = $recordObj2->createMergeHash($tagslib); + + # Parameters + $template->param( + recordid1 => $mergereference, + recordid2 => $notreference, + record1 => @record1, + record2 => @record2, + framework => $framework, + ); + } + else { + + # Ask the user to choose which record will be the kept + $template->param( + choosereference => 1, + recordid1 => $authid[0], + recordid2 => $authid[1], + title1 => $recordObj1->authorized_heading, + title2 => $recordObj2->authorized_heading, + ); + if ( $recordObj1->authtype ne $recordObj2->authtype ) { + my $frameworks = getauthtypes; + my @frameworkselect; + foreach my $thisframeworkcode ( keys %$frameworks ) { + my %row = ( + value => $thisframeworkcode, + frameworktext => + $frameworks->{$thisframeworkcode}->{'authtypetext'}, + ); + if ( $recordObj1->authtype eq $thisframeworkcode ) { + $row{'selected'} = 1; + } + push @frameworkselect, \%row; + } + $template->param( + frameworkselect => \@frameworkselect, + frameworkcode1 => $recordObj1->authtype, + frameworkcode2 => $recordObj2->authtype, + ); + } + } + } +} + +if (@errors) { + + # Errors + $template->param( errors => \@errors ); +} + +output_html_with_http_headers $input, $cookie, $template->output; +exit; + +=head1 FUNCTIONS + +=cut + +# ------------------------ +# Functions +# ------------------------ --- a/authorities/merge_ajax.pl +++ a/authorities/merge_ajax.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use CGI; +use CGI::Session; +use C4::Context; +use C4::Auth qw/check_cookie_auth/; +use C4::AuthoritiesMarc; +use JSON; +use CGI::Cookie; # need to check cookies before + # having CGI parse the POST request + +my %cookies = fetch CGI::Cookie; +my ($auth_status, $sessionID) = check_cookie_auth($cookies{'CGISESSID'}->value, { editcatalogue => 'edit_catalogue' }); +if ($auth_status ne "ok") { + my $reply = CGI->new(""); + print $reply->header(-type => 'text/html'); + exit 0; +} + +my $reply = new CGI; +my $framework = $reply->param('frameworkcode'); +my $tagslib = GetTagsLabels(1, $framework); +print $reply->header(-type => 'text/html'); +print encode_json $tagslib; --- a/koha-tmpl/intranet-tmpl/prog/en/includes/authorities_js.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/authorities_js.inc @@ -0,0 +1,39 @@ +function mergeAuth(authid, summary) { + var alreadySelected = $.cookie('auth_to_merge'); + if (alreadySelected !== null) { + alreadySelected = JSON.parse(alreadySelected); + $.cookie('auth_to_merge', '', { 'path': '/', 'expires': -1 }); + var refstring = ""; + if (typeof alreadySelected.mergereference !== 'undefined') { + refstring = "&mergereference=" + alreadySelected.mergereference; + } + window.location.href = "/cgi-bin/koha/authorities/merge.pl?authid=" + authid + "&authid=" + alreadySelected.authid + refstring; + } else { + $.cookie('auth_to_merge', JSON.stringify({ 'authid': authid, 'summary': summary }), { 'path' : '/' }); + showMergingInProgress(); + } +} + +function showMergingInProgress() { + var alreadySelected = $.cookie('auth_to_merge'); + if (alreadySelected !== null) { + alreadySelected = JSON.parse(alreadySelected); + $('#merge_in_progress').html(_("Merging with authority: ") + " (" + alreadySelected.authid + ")" + _("Cancel merge") + ""); + $('#cancel_merge').click(function(event) { + event.preventDefault(); + $.cookie('auth_to_merge', '', { 'path': '/', 'expires': -1 }); + $('#merge_in_progress').empty(); + }); + } else { + $('#merge_in_progress').empty(); + } +} + +$(document).ready(function () { + showMergingInProgress(); + $('.merge_auth').click(function (event) { + event.preventDefault(); + mergeAuth($(this).parents('tr').attr('data-authid'), $(this).parents('tr').find('div.authorizedheading').text()); + }); +}); + --- a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities-home.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities-home.tt @@ -19,6 +19,7 @@ function searchauthority() { function confirm_deletion() { // not really implemented, but required by phantom delAuthButton code in authorities-toolbar.inc return true; } +[% INCLUDE 'authorities_js.inc' %] //]]> @@ -35,6 +36,8 @@ function confirm_deletion() { // not really implemented, but required by phant [% INCLUDE 'authorities-toolbar.inc' %] +
+ --- a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/merge.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/merge.tt @@ -0,0 +1,143 @@ +[% PROCESS 'merge-record.inc' %] +[% INCLUDE 'doc-head-open.inc' %] +The merging was successful. Click here to see the merged record.
+ [% END %] + +[% ELSE %] + +[% IF ( choosereference ) %] +Please choose which record will be the reference for the merge. The record chosen as reference will be kept, and the other will be deleted.
+ +[% ELSE %] +[% IF ( errors ) %] ++ [% IF error.code == 'WRONG_COUNT' %] + Number of records provided for merging: [% error.value %]. Currently only 2 records can be merged at a time. + [% ELSE %] + [% error %] + [% END %] + +
+ [% END %] +