Browse Source

Bug 10096 - Add a Z39.50 interface for authority searching

This patch introduces a new Z39.50 interface for searching Z39.50
compliant databases for MARC authority records.

These databases aren't as common as their bibliographic equivalents,
but they're out there and very useful. I have included info at the
bottom of this messsage for sample authority databases you can try.

To test this patch:

1) Set up Z39.50 client targets for authority databases. (I've included
information at the bottom of this message for LibrariesAustralia's
test server for authorities as well as instructions on how to use
your Koha's z39.50 authority server as well. The Library of Congress
also has authority databases available (unsure if these are test or
prod), and you might have access to others through OCLC or RLIN. OCLC
provides login credentials for their test databases.

2) Go to the Authorities module

3) Click on the new "Z39.50 search button"

4) Select your authority search targets from the list.

5) Do a search for an authority you would like using either the "Raw"
input box or the more specific input boxes for names, subjects, subject
sub divisions, or titles. (I like searching Name (personal): Eric on
the LibrariesAustralia test DB.)

6) You should see a table listing the server, heading, authority type,
and two other columns (MARC and a nameless column). "Authority type"
is the type of authority it will become when imported in to Koha. In
the Eric example, "PERSO_NAME".

7) Click on "MARC" next to the results of interest to review the MARC
authority record.

8) When you're satisfied with a record, click on "Import".

9) The pop-up window will close and your original Koha window will
change to the "Adding authority Personal Name" screen (in the Eric
example).

10) All the relevant fields should be filled out for the record. Review
them and make any changes as necessary. (N.B. The 001 will be cleared
when saved, so if you have a use for the imported control number, move
it to the 010, 016, or 035 as appropriate. If you have a default value
for the 003, this will also likely be overwritten. Move it if necessary.
The 005 will also be updated when saved, so do not worry about that.)

11) When you're satisfied, click save.

12) Presto! You've imported your first authority record via Z39.50!

--

Here is the info for the LibrariesAustralia test Z39.50 authority
database:

Z39.50 server: LibrariesAustralia Authorities
Hostname: z3950-test.librariesaustralia.nla.gov.au
Port: 210
Database: AuthTraining
Userid: ANLEZ
Password: z39.50
Syntax: MARC21/USMARC
Encoding: utf8

-

The U.S.A. Library of Congress also provides Z39.50 access to its Name
and Subject Authorities (http://www.loc.gov/z3950/lcserver.html).

Name Authority:
Z39.50 server: Library of Congress Name Authority File
Hostname: lx2.loc.gov
Port: 210
Database: NAF
Syntax: MARC21/USMARC
Encoding: utf8

Subject Authority:
Z39.50 server: Library of Congress Subject Authority File
Hostname: lx2.loc.gov
Port: 210
Database: SAF
Syntax: MARC21/USMARC
Encoding: utf8

(N.B. Both of these databases also include title authorities.)

-

For testing purposes, you can also set up a Z39.50 client target,
which points at your own Koha instance's Z39.50 authority server.

To find the hostname, go to /etc/koha-conf.xml and find the value for
the <listen id="authorityserver"> element. Depending on your
configuration, this could be something like the following:

unix:/zebra/koha/var/run/zebradb/authoritysocket

(N.B. You might be using a different scheme than unix sockets...)

To find the database, scroll down to the bottom of koha-conf.xml until
you reach the <config> element. Within this, look for the value of the
element <authorityserver>. It should probably be "authorities".

To set up this Z39.50 client target in Koha...

Z39.50 server: my koha authorities
Hostname: unix:/zebra/koha/var/run/zebradb/authoritysocket
Port:
Database: authorities
Userid:
Password:
Syntax: MARC21/USMARC (or whichever flavour you need)
Encoding: utf8

Signed-off-by: Mason James <mtj@kohaaloha.com>

Bug 10096 [FOLLOW-UP] - Add a z39.50 interface for authority searching

This patch adds the "recordtype" column to the "z3950servers" table.

The value in this column (biblio or authority) then controls whether
the z3950 server shows up in a bibliographic search (through the
Acq and Cataloguing modules) or in an authority search (through
the Authorities module).

I also edited the z3950 management console to show this value
and allow users to edit it. The default value is "biblio", since
the vast majority of z3950 targets will be bibliographic. However,
there is an option to add/edit a z3950 target as a source of
authority records.

Test Plan:

1) Apply both patches
2) Run updatedatabase.pl (after setting your KOHA_CONF and PERL5
environmental variables)
3) Use the test plan from the 1st patch

N.B. Make sure that your Z39.50 client target has a Record Type
of Authority, otherwise it won't display when you're doing a
Z3950 search for authorities.

Signed-off-by: Mason James <mtj@kohaaloha.com>

Bug 10096 [FOLLOW-UP] - fix tabs/whitespace errors to pass QA

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>
new/bootstrap-opac
David Cook 10 years ago
committed by Galen Charlton
parent
commit
1e0b890b0c
  1. 305
      C4/Breeding.pm
  2. 2
      acqui/z3950_search.pl
  3. 115
      admin/z3950servers.pl
  4. 28
      authorities/authorities.pl
  5. 108
      cataloguing/z3950_auth_search.pl
  6. 2
      cataloguing/z3950_search.pl
  7. 1
      installer/data/mysql/kohastructure.sql
  8. 7
      installer/data/mysql/updatedatabase.pl
  9. 9
      koha-tmpl/intranet-tmpl/prog/en/includes/authorities-toolbar.inc
  10. 25
      koha-tmpl/intranet-tmpl/prog/en/modules/admin/z3950servers.tt
  11. 227
      koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/z3950_auth_search.tt

305
C4/Breeding.pm

@ -1,6 +1,7 @@
package C4::Breeding;
# Copyright 2000-2002 Katipo Communications
# Parts Copyright 2013 Prosentient Systems
#
# This file is part of Koha.
#
@ -25,6 +26,7 @@ use C4::Koha;
use C4::Charset;
use MARC::File::USMARC;
use C4::ImportBatch;
use C4::AuthoritiesMarc; #GuessAuthTypeCode, FindDuplicateAuthority
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@ -33,7 +35,7 @@ BEGIN {
$VERSION = 3.07.00.049;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(&ImportBreeding &BreedingSearch &Z3950Search);
@EXPORT = qw(&ImportBreeding &BreedingSearch &Z3950Search &Z3950SearchAuth);
}
=head1 NAME
@ -451,6 +453,307 @@ sub _isbn_replace {
return $isbn;
}
=head2 ImportBreedingAuth
ImportBreedingAuth($marcrecords,$overwrite_auth,$filename,$encoding,$z3950random,$batch_type);
TODO description
=cut
sub ImportBreedingAuth {
my ($marcrecords,$overwrite_auth,$filename,$encoding,$z3950random,$batch_type) = @_;
my @marcarray = split /\x1D/, $marcrecords;
my $dbh = C4::Context->dbh;
my $batch_id = GetZ3950BatchId($filename);
my $searchbreeding = $dbh->prepare("select import_record_id from import_auths where control_number=? and authorized_heading=?");
# $encoding = C4::Context->preference("marcflavour") unless $encoding;
# fields used for import results
my $imported=0;
my $alreadyindb = 0;
my $alreadyinfarm = 0;
my $notmarcrecord = 0;
my $breedingid;
for (my $i=0;$i<=$#marcarray;$i++) {
my ($marcrecord, $charset_result, $charset_errors);
($marcrecord, $charset_result, $charset_errors) =
MarcToUTF8Record($marcarray[$i]."\x1D", C4::Context->preference("marcflavour"), $encoding);
# Normalize the record so it doesn't have separated diacritics
SetUTF8Flag($marcrecord);
# warn "$i : $marcarray[$i]";
# FIXME - currently this does nothing
my @warnings = $marcrecord->warnings();
if (scalar($marcrecord->fields()) == 0) {
$notmarcrecord++;
} else {
my $heading;
$heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marcrecord });
my $heading_authtype_code;
$heading_authtype_code = GuessAuthTypeCode($marcrecord);
my $controlnumber;
$controlnumber = $marcrecord->field('001')->data;
#Check if the authority record already exists in the database...
my ($duplicateauthid,$duplicateauthvalue);
if ($marcrecord && $heading_authtype_code) {
($duplicateauthid,$duplicateauthvalue) = FindDuplicateAuthority( $marcrecord, $heading_authtype_code);
}
if ($duplicateauthid && $overwrite_auth ne 2) {
#If the authority record exists and $overwrite_auth doesn't equal 2, then mark it as already in the DB
#FIXME: What does $overwrite_auth = 2 even mean?
#FIXME: Should we bother with $overwrite_auth values? Currently, the hard-coded $overwrite_auth value is 2, which means the database gets filled with import_records...
#^^ of course, we might not want to reject records if their control number/heading exist in the db or breeding/import pool...as we might be wanting to update existing authority records...
$alreadyindb++;
} else {
if ($controlnumber && $heading) {
$searchbreeding->execute($controlnumber,$heading);
($breedingid) = $searchbreeding->fetchrow;
}
if ($breedingid && $overwrite_auth eq '0') {
#FIXME: What does $overwrite_auth = 0 even mean?
$alreadyinfarm++;
} else {
if ($breedingid && $overwrite_auth eq '1') {
#FIXME: What does $overwrite_auth = 1 even mean?
ModAuthorityInBatch($breedingid, $marcrecord);
} else {
my $import_id = AddAuthToBatch($batch_id, $imported, $marcrecord, $encoding, $z3950random);
$breedingid = $import_id;
}
$imported++;
}
}
}
}
return ($notmarcrecord,$alreadyindb,$alreadyinfarm,$imported,$breedingid);
}
=head2 Z3950SearchAuth
Z3950SearchAuth($pars, $template);
Parameters for Z3950 search are all passed via the $pars hash. It may contain nameany, namepersonal, namecorp, namemeetingcon,
title, uniform title, subject, subjectsubdiv, srchany.
Also it should contain an arrayref id that points to a list of IDs of the z3950 targets to be queried (see z3950servers table).
This code is used in cataloging/z3950_auth_search.
The second parameter $template is a Template object. The routine uses this parameter to store the found values into the template.
=cut
sub Z3950SearchAuth {
my ($pars, $template)= @_;
my $dbh = C4::Context->dbh;
my @id= @{$pars->{id}};
my $random= $pars->{random};
my $page= $pars->{page};
my $nameany= $pars->{nameany};
my $authorany= $pars->{authorany};
my $authorpersonal= $pars->{authorpersonal};
my $authorcorp= $pars->{authorcorp};
my $authormeetingcon= $pars->{authormeetingcon};
my $title= $pars->{title};
my $uniformtitle= $pars->{uniformtitle};
my $subject= $pars->{subject};
my $subjectsubdiv= $pars->{subjectsubdiv};
my $srchany= $pars->{srchany};
my $show_next = 0;
my $total_pages = 0;
my $attr = '';
my $host;
my $server;
my $database;
my $port;
my $marcdata;
my @encoding;
my @results;
my $count;
my $record;
my @serverhost;
my @servername;
my @breeding_loop = ();
my @oConnection;
my @oResult;
my @errconn;
my $s = 0;
my $query;
my $nterms=0;
if ($nameany) {
$query .= " \@attr 1=1002 \"$nameany\" "; #Any name (this includes personal, corporate, meeting/conference authors, and author names in subject headings)
#This attribute is supported by both the Library of Congress and Libraries Australia 08/05/2013
$nterms++;
}
if ($authorany) {
$query .= " \@attr 1=1003 \"$authorany\" "; #Author-name (this includes personal, corporate, meeting/conference authors, but not author names in subject headings)
#This attribute is not supported by the Library of Congress, but is supported by Libraries Australia 08/05/2013
$nterms++;
}
if ($authorcorp) {
$query .= " \@attr 1=2 \"$authorcorp\" "; #1005 is another valid corporate author attribute...
$nterms++;
}
if ($authorpersonal) {
$query .= " \@attr 1=1 \"$authorpersonal\" "; #1004 is another valid personal name attribute...
$nterms++;
}
if ($authormeetingcon) {
$query .= " \@attr 1=3 \"$authormeetingcon\" "; #1006 is another valid meeting/conference name attribute...
$nterms++;
}
if ($subject) {
$query .= " \@attr 1=21 \"$subject\" ";
$nterms++;
}
if ($subjectsubdiv) {
$query .= " \@attr 1=47 \"$subjectsubdiv\" ";
$nterms++;
}
if ($title) {
$query .= " \@attr 1=4 \"$title\" "; #This is a regular title search. 1=6 will give just uniform titles
$nterms++;
}
if ($uniformtitle) {
$query .= " \@attr 1=6 \"$uniformtitle\" "; #This is the uniform title search
$nterms++;
}
if($srchany) {
$query .= " \@attr 1=1016 \"$srchany\" ";
$nterms++;
}
for my $i (1..$nterms-1) {
$query = "\@and " . $query;
}
foreach my $servid (@id) {
my $sth = $dbh->prepare("select * from z3950servers where id=?");
$sth->execute($servid);
while ( $server = $sth->fetchrow_hashref ) {
my $option1 = new ZOOM::Options();
$option1->option( 'async' => 1 );
$option1->option( 'elementSetName', 'F' );
$option1->option( 'databaseName', $server->{db} );
$option1->option( 'user', $server->{userid} ) if $server->{userid};
$option1->option( 'password', $server->{password} ) if $server->{password};
$option1->option( 'preferredRecordSyntax', $server->{syntax} );
$option1->option( 'timeout', $server->{timeout} ) if $server->{timeout};
$oConnection[$s] = create ZOOM::Connection($option1);
$oConnection[$s]->connect( $server->{host}, $server->{port} );
$serverhost[$s] = $server->{host};
$servername[$s] = $server->{name};
$encoding[$s] = ($server->{encoding}?$server->{encoding}:"iso-5426");
$s++;
} ## while fetch
} # foreach
my $nremaining = $s;
for ( my $z = 0 ; $z < $s ; $z++ ) {
$oResult[$z] = $oConnection[$z]->search_pqf($query);
}
while ( $nremaining-- ) {
my $k;
my $event;
while ( ( $k = ZOOM::event( \@oConnection ) ) != 0 ) {
$event = $oConnection[ $k - 1 ]->last_event();
last if $event == ZOOM::Event::ZEND;
}
if ( $k != 0 ) {
$k--;
my ($error, $errmsg, $addinfo, $diagset)= $oConnection[$k]->error_x();
if ($error) {
if ($error =~ m/^(10000|10007)$/ ) {
push(@errconn, {'server' => $serverhost[$k]});
}
}
else {
my $numresults = $oResult[$k]->size();
my $i;
my $result = '';
if ( $numresults > 0 and $numresults >= (($page-1)*20)) {
$show_next = 1 if $numresults >= ($page*20);
$total_pages = int($numresults/20)+1 if $total_pages < ($numresults/20);
for ($i = ($page-1)*20; $i < (($numresults < ($page*20)) ? $numresults : ($page*20)); $i++) {
my $rec = $oResult[$k]->record($i);
if ($rec) {
my $marcrecord;
my $marcdata;
$marcdata = $rec->raw();
my ($charset_result, $charset_errors);
($marcrecord, $charset_result, $charset_errors)= MarcToUTF8Record($marcdata, C4::Context->preference('marcflavour'), $encoding[$k]);
my $heading;
my $heading_authtype_code;
$heading_authtype_code = GuessAuthTypeCode($marcrecord);
$heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marcrecord });
my ($notmarcrecord, $alreadyindb, $alreadyinfarm, $imported, $breedingid)= ImportBreedingAuth( $marcdata, 2, $serverhost[$k], $encoding[$k], $random, 'z3950' );
my %row_data;
$row_data{server} = $servername[$k];
$row_data{breedingid} = $breedingid;
$row_data{heading} = $heading;
$row_data{heading_code} = $heading_authtype_code;
push( @breeding_loop, \%row_data );
}
else {
push(@breeding_loop,{'server'=>$servername[$k],'title'=>join(': ',$oConnection[$k]->error_x()),'breedingid'=>-1});
}
}
} #if $numresults
}
} # if $k !=0
$template->param(
numberpending => $nremaining,
current_page => $page,
total_pages => $total_pages,
show_nextbutton => $show_next?1:0,
show_prevbutton => $page!=1,
);
} # while nremaining
#close result sets and connections
foreach(0..$s-1) {
$oResult[$_]->destroy();
$oConnection[$_]->destroy();
}
my @servers = ();
foreach my $id (@id) {
push @servers, {id => $id};
}
$template->param(
breeding_loop => \@breeding_loop,
servers => \@servers,
errconn => \@errconn
);
}
1;
__END__

2
acqui/z3950_search.pl

@ -93,7 +93,7 @@ $template->param(
);
if ( $op ne "do_search" ) {
my $sth = $dbh->prepare("select id,host,name,checked from z3950servers order by host");
my $sth = $dbh->prepare("select id,host,name,checked from z3950servers where recordtype <> 'authority' order by host");
$sth->execute();
my $serverloop = $sth->fetchall_arrayref( {} );
$template->param(

115
admin/z3950servers.pl

@ -40,7 +40,7 @@ sub StringSearch {
$searchstring = '';
}
my $query = "SELECT host,port,db,userid,password,name,id,checked,rank,syntax,encoding,timeout";
my $query = "SELECT host,port,db,userid,password,name,id,checked,rank,syntax,encoding,timeout,recordtype";
$query .= " FROM z3950servers";
if ( $searchstring ne '' ) { $query .= " WHERE (name like ?)" }
$query .= " ORDER BY rank,name";
@ -85,24 +85,27 @@ $template->param(script_name => $script_name,
################## ADD_FORM ##################################
# called by default. Used to create form to add or modify a record
if ($op eq 'add_form') {
$template->param(add_form => 1);
#---- if primkey exists, it's a modify action, so read values to modify...
my $data;
if ($searchfield) {
my $dbh = C4::Context->dbh;
my $sth=$dbh->prepare("select host,port,db,userid,password,name,id,checked,rank,syntax,encoding,timeout from z3950servers where (name = ?) order by rank,name");
$sth->execute($searchfield);
$data=$sth->fetchrow_hashref;
$sth->finish;
}
$template->param( $_ => $data->{$_} )
for ( qw( host port db userid password checked rank timeout encoding ) );
$template->param( $_ . $data->{$_} => 1)
for ( qw( syntax ) );
# END $OP eq ADD_FORM
if ( $op eq 'add_form' ) {
$template->param( add_form => 1 );
#---- if primkey exists, it's a modify action, so read values to modify...
my $data;
if ($searchfield) {
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare(
"select host,port,db,userid,password,name,id,checked,rank,syntax,encoding,timeout,recordtype from z3950servers where (name = ?) order by rank,name"
);
$sth->execute($searchfield);
$data = $sth->fetchrow_hashref;
$sth->finish;
}
$template->param( $_ => $data->{$_} )
for (qw( host port db userid password checked rank timeout encoding ));
$template->param( $_ . $data->{$_} => 1 ) for (qw( syntax recordtype ));
# END $OP eq ADD_FORM
################## ADD_VALIDATE ##################################
# called by add_form, used to insert/modify data in DB
# called by add_form, used to insert/modify data in DB
} elsif ($op eq 'add_validate') {
my $dbh=C4::Context->dbh;
my $sth=$dbh->prepare("select * from z3950servers where name=?");
@ -110,7 +113,7 @@ if ($op eq 'add_form') {
my $checked = $input->param('checked') ? 1 : 0;
if ($sth->rows) {
$template->param(confirm_update => 1);
$sth=$dbh->prepare("update z3950servers set host=?, port=?, db=?, userid=?, password=?, name=?, checked=?, rank=?,syntax=?,encoding=?,timeout=? where name=?");
$sth=$dbh->prepare("update z3950servers set host=?, port=?, db=?, userid=?, password=?, name=?, checked=?, rank=?,syntax=?,encoding=?,timeout=?,recordtype=? where name=?");
$sth->execute($input->param('host'),
$input->param('port'),
$input->param('db'),
@ -122,6 +125,7 @@ if ($op eq 'add_form') {
$input->param('syntax'),
$input->param('encoding'),
$input->param('timeout'),
$input->param('recordtype'),
$input->param('searchfield'),
);
}
@ -129,44 +133,46 @@ if ($op eq 'add_form') {
$template->param(confirm_add => 1);
$sth=$dbh->prepare(
"INSERT INTO z3950servers " .
"(host,port,db,userid,password,name,checked,rank,syntax,encoding,timeout) " .
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" );
"(host,port,db,userid,password,name,checked,rank,syntax,encoding,timeout,recordtype) " .
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" );
$sth->execute(
$input->param( 'host' ),
$input->param( 'port' ),
$input->param( 'db' ),
$input->param( 'userid' ),
$input->param( 'password' ),
$input->param( 'searchfield' ),
$checked,
$input->param( 'rank' ),
$input->param( 'syntax' ),
$input->param( 'encoding' ),
$input->param( 'timeout' ) );
}
$sth->finish;
# END $OP eq ADD_VALIDATE
$input->param('host'), $input->param('port'),
$input->param('db'), $input->param('userid'),
$input->param('password'), $input->param('searchfield'),
$checked, $input->param('rank'),
$input->param('syntax'), $input->param('encoding'),
$input->param('timeout'), $input->param('recordtype')
);
}
$sth->finish;
# END $OP eq ADD_VALIDATE
################## DELETE_CONFIRM ##################################
# called by default form, used to confirm deletion of data in DB
} elsif ($op eq 'delete_confirm') {
$template->param(delete_confirm => 1);
my $dbh = C4::Context->dbh;
my $sth2=$dbh->prepare("select host,port,db,userid,password,name,id,checked,rank,syntax,encoding,timeout from z3950servers where (name = ?) order by rank,name");
$sth2->execute($searchfield);
my $data=$sth2->fetchrow_hashref;
$sth2->finish;
$template->param(host => $data->{'host'},
port => $data->{'port'},
db => $data->{'db'},
userid => $data->{'userid'},
password => $data->{'password'},
checked => $data->{'checked'},
rank => $data->{'rank'},
syntax => $data->{'syntax'},
timeout => $data->{'timeout'},
encoding => $data->{'encoding'} );
$template->param( delete_confirm => 1 );
my $dbh = C4::Context->dbh;
my $sth2 = $dbh->prepare(
"select host,port,db,userid,password,name,id,checked,rank,syntax,encoding,timeout,recordtype from z3950servers where (name = ?) order by rank,name"
);
$sth2->execute($searchfield);
my $data = $sth2->fetchrow_hashref;
$sth2->finish;
$template->param(
host => $data->{'host'},
port => $data->{'port'},
db => $data->{'db'},
userid => $data->{'userid'},
password => $data->{'password'},
checked => $data->{'checked'},
rank => $data->{'rank'},
syntax => $data->{'syntax'},
timeout => $data->{'timeout'},
recordtype => $data->{'recordtype'},
encoding => $data->{'encoding'}
);
# END $OP eq DELETE_CONFIRM
################## DELETE_CONFIRMED ##################################
@ -197,7 +203,8 @@ if ($op eq 'add_form') {
rank => $results->[$i]{'rank'},
syntax => $results->[$i]{'syntax'},
encoding => $results->[$i]{'encoding'},
timeout => $results->[$i]{'timeout'});
timeout => $results->[$i]{'timeout'},
recordtype => $results->[$i]{'recordtype'});
push @loop, \%row;
}

28
authorities/authorities.pl

@ -24,6 +24,7 @@ use CGI;
use C4::Auth;
use C4::Output;
use C4::AuthoritiesMarc;
use C4::ImportBatch; #GetImportRecordMarc
use C4::Context;
use C4::Koha; # XXX subfield_is_koha_internal_p
use Date::Calc qw(Today);
@ -47,6 +48,21 @@ builds list, depending on authorised value...
=cut
sub MARCfindbreeding_auth {
my ( $id ) = @_;
my ($marc, $encoding) = GetImportRecordMarc($id);
if ($marc) {
my $record = MARC::Record->new_from_usmarc($marc);
if ( !defined(ref($record)) ) {
return -1;
} else {
return $record, $encoding;
}
} else {
return -1;
}
}
sub build_authorized_values_list {
my ( $tag, $subfield, $value, $dbh, $authorised_values_sth,$index_tag,$index_subfield ) = @_;
@ -544,6 +560,7 @@ my $nonav = $input->param('nonav');
my $myindex = $input->param('index');
my $linkid=$input->param('linkid');
my $authtypecode = $input->param('authtypecode');
my $breedingid = $input->param('breedingid');
my $dbh = C4::Context->dbh;
if(!$authtypecode) {
@ -558,11 +575,18 @@ my ($template, $loggedinuser, $cookie)
flagsrequired => {editauthorities => 1},
debug => 1,
});
$template->param(nonav => $nonav,index=>$myindex,authtypecode=>$authtypecode,);
$template->param(nonav => $nonav,index=>$myindex,authtypecode=>$authtypecode,breedingid=>$breedingid,);
$tagslib = GetTagsLabels(1,$authtypecode);
my $record=-1;
my $encoding="";
$record = GetAuthority($authid) if ($authid);
if (($authid) && !($breedingid)){
$record = GetAuthority($authid);
}
if ($breedingid) {
( $record, $encoding ) = MARCfindbreeding_auth( $breedingid );
}
my ($oldauthnumtagfield,$oldauthnumtagsubfield);
my ($oldauthtypetagfield,$oldauthtypetagsubfield);
$is_a_modif=0;

108
cataloguing/z3950_auth_search.pl

@ -0,0 +1,108 @@
#!/usr/bin/perl
# This is a completely new Z3950 clients search using async ZOOM -TG 02/11/06
# Copyright 2000-2002 Katipo Communications
#
# This is a new Z3950 authority search using the current Z3950 bibliographic search as a model 07/05/2013
# Parts Copyright 2013 Prosentient Systems
#
# 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 qw / -utf8 /;
use C4::Auth;
use C4::Output;
use C4::Context;
use C4::Breeding;
use C4::Koha;
my $input = new CGI;
my $dbh = C4::Context->dbh;
my $error = $input->param('error');
my $nameany = $input->param('nameany');
my $authorany = $input->param('authorany');
my $authorcorp = $input->param('authorcorp');
my $authorpersonal = $input->param('authorpersonal');
my $authormeetingcon = $input->param('authormeetingcon');
my $title = $input->param('title');
my $uniformtitle = $input->param('uniformtitle');
my $subject = $input->param('subject');
my $subjectsubdiv = $input->param('subjectsubdiv');
my $srchany = $input->param('srchany');
my $op = $input->param('op')||'';
my $page = $input->param('current_page') || 1;
$page = $input->param('goto_page') if $input->param('changepage_goto');
my ( $template, $loggedinuser, $cookie ) = get_template_and_user({
template_name => "cataloguing/z3950_auth_search.tmpl",
query => $input,
type => "intranet",
authnotrequired => 1,
flagsrequired => { catalogue => 1 },
});
$template->param(
nameany => $nameany,
authorany => $authorany,
authorcorp => $authorcorp,
authorpersonal => $authorpersonal,
authormeetingcon => $authormeetingcon,
title => $title,
uniformtitle => $uniformtitle,
subject => $subject,
subjectsubdiv => $subjectsubdiv,
srchany => $srchany,
);
if ( $op ne "do_search" ) {
my $sth = $dbh->prepare("SELECT id,host,name,checked FROM z3950servers WHERE recordtype = 'authority' ORDER BY rank, name");
$sth->execute();
my $serverloop = $sth->fetchall_arrayref( {} );
$template->param(
serverloop => $serverloop,
opsearch => "search",
);
output_html_with_http_headers $input, $cookie, $template->output;
exit;
}
my @id = $input->param('id');
if ( @id==0 ) {
# empty server list -> report and exit
$template->param( emptyserverlist => 1 );
output_html_with_http_headers $input, $cookie, $template->output;
exit;
}
my $pars= {
random => $input->param('random') || rand(1000000000),
page => $page,
id => \@id,
nameany => $nameany,
authorany => $authorany,
authorcorp => $authorcorp,
authorpersonal => $authorpersonal,
authormeetingcon => $authormeetingcon,
title => $title,
uniformtitle => $uniformtitle,
subject => $subject,
subjectsubdiv => $subjectsubdiv,
srchany => $srchany,
};
Z3950SearchAuth($pars, $template);
output_html_with_http_headers $input, $cookie, $template->output;

2
cataloguing/z3950_search.pl

@ -74,7 +74,7 @@ $template->param(
);
if ( $op ne "do_search" ) {
my $sth = $dbh->prepare("SELECT id,host,name,checked FROM z3950servers ORDER BY rank, name");
my $sth = $dbh->prepare("SELECT id,host,name,checked FROM z3950servers WHERE recordtype <> 'authority' ORDER BY rank, name");
$sth->execute();
my $serverloop = $sth->fetchall_arrayref( {} );
$template->param(

1
installer/data/mysql/kohastructure.sql

@ -2282,6 +2282,7 @@ CREATE TABLE `z3950servers` ( -- connection information for the Z39.50 targets u
`type` enum('zed','opensearch') NOT NULL default 'zed',
`encoding` text default NULL, -- characters encoding provided by this target
`description` text NOT NULL, -- unused in Koha
`recordtype` varchar(45) NOT NULL default 'biblio', -- server contains bibliographic or authority records
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

7
installer/data/mysql/updatedatabase.pl

@ -7162,6 +7162,13 @@ if ( CheckVersion($DBversion) ) {
SetVersion($DBversion);
}
$DBversion = "3.13.00.XXX";
if ( CheckVersion($DBversion) ) {
$dbh->do(q{ALTER TABLE `z3950servers` ADD COLUMN `recordtype` VARCHAR(45) NOT NULL DEFAULT 'biblio';});
print "Upgrade to $DBversion done (Bug 10096 - Add a Z39.50 interface for authority searching)\n";
SetVersion ($DBversion);
}
=head1 FUNCTIONS
=head2 TableExists($table)

9
koha-tmpl/intranet-tmpl/prog/en/includes/authorities-toolbar.inc

@ -5,6 +5,12 @@
confirm_deletion();
return false;
});
$("#z3950submit").click(function(){
window.open("/cgi-bin/koha/cataloguing/z3950_auth_search.pl","z3950search",'width=740,height=450,location=yes,toolbar=no,scrollbars=yes,resize=yes');
return false;
});
});
//]]>
@ -47,6 +53,9 @@
[% END %]
</ul>
</div>
<div class="btn-group">
<a class="btn btn-small" id="z3950submit" href="#"><i class="icon-search"></i> Z39.50 search</a>
</div>
[% END %]
</div>

25
koha-tmpl/intranet-tmpl/prog/en/modules/admin/z3950servers.tt

@ -238,6 +238,20 @@
<li><label for="timeout">Timeout (0 its like not set): </label>
<input type="text" name="timeout" id="timeout" size="4" value="[% timeout %]" onblur="isNum(this)" /> seconds
</li>
<li><label for="recordtype">Record type: </label>
<select name="recordtype" id="recordtype">
[% IF ( recordtypebiblio ) %]
<option value="biblio" selected="selected">Bibliographic</option>
[% ELSE %]
<option value="biblio">Bibliographic</option>
[% END %]
[% IF ( recordtypeauthority ) %]
<option value="authority" selected="selected">Authority</option>
[% ELSE %]
<option value="authority">Authority</option>
[% END %]
</select>
</li>
</ol>
</fieldset>
@ -274,6 +288,7 @@
<li><strong>Syntax: </strong>[% syntax %]</li>
<li><strong>Encoding: </strong>[% encoding %]</li>
<li><strong>Timeout: </strong>[% timeout %]</li>
<li><strong>Record type: </strong>[% recordtype %]</li>
</ul> <form action="[% script_name %]" method="post"><input type="hidden" name="op" value="delete_confirmed" /><input type="hidden" name="searchfield" value="[% searchfield %]" /><input type="submit" value="Delete this server" /></form> <form action="[% script_name %]" method="post"><input type="submit" value="Do not delete" /></form>
@ -297,9 +312,9 @@
[% IF ( searchfield ) %]
You searched for [% searchfield %]
[% END %]
[% END %]
<table id="serverst">
<thead><tr><th>Target</th><th>Hostname/Port</th><th>Database</th><th>Userid</th><th>Password</th><th>Checked</th><th>Rank</th><th>Syntax</th><th>Encoding</th><th>Timeout</th><th>&nbsp;</th><th>&nbsp;</th>
<thead><tr><th>Target</th><th>Hostname/Port</th><th>Database</th><th>Userid</th><th>Password</th><th>Checked</th><th>Rank</th><th>Syntax</th><th>Encoding</th><th>Timeout</th><th>Record type</th><th>&nbsp;</th><th>&nbsp;</th>
</tr></thead>
<tbody>[% FOREACH loo IN loop %]
[% UNLESS ( loop.odd ) %]
@ -307,9 +322,11 @@
[% ELSE %]
<tr>
[% END %]
<td><a href="[% loo.script_name %]?op=add_form&amp;searchfield=[% loo.name |url %]">[% loo.name %]</a></td><td>[% loo.host %]:[% loo.port %]</td><td>[% loo.db %]</td><td>[% loo.userid %]</td><td>[% loo.password %]</td><td>[% IF ( loo.checked ) %]Yes[% ELSE %]No[% END %]</td><td>[% loo.rank %]</td> <td>[% loo.syntax %]</td><td>[% loo.encoding %]</td><td>[% loo.timeout %]</td><td><a href="[% loo.script_name %]?op=add_form&amp;searchfield=[% loo.name |url %]">Edit</a></td><td><a href="[% loo.script_name %]?op=delete_confirm&amp;searchfield=[% loo.name |url %]">Delete</a></td> </tr>
<td><a href="[% loo.script_name %]?op=add_form&amp;searchfield=[% loo.name |url %]">[% loo.name %]</a></td><td>[% loo.host %]:[% loo.port %]</td><td>[% loo.db %]</td><td>[% loo.userid %]</td><td>[% loo.password %]</td><td>[% IF ( loo.checked ) %]Yes[% ELSE %]No[% END %]</td><td>[% loo.rank %]</td>
<td>[% loo.syntax %]</td><td>[% loo.encoding %]</td><td>[% loo.timeout %]</td><td>[% loo.recordtype %]</td><td><a href="[% loo.script_name %]?op=add_form&amp;searchfield=[% loo.name |url %]">Edit</a></td><td><a href="[% loo.script_name %]?op=delete_confirm&amp;searchfield=[% loo.name |url %]">Delete</a></td> </tr>
[% END %]</tbody>
</table>
</table>
[% IF ( offsetgtzero ) %]<form action="[% script_name %]" method="get">
<input type="hidden" name="offset" value="[% prevpage %]" />

227
koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/z3950_auth_search.tt

@ -0,0 +1,227 @@
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha &rsaquo; Z39.50 search results</title>
[% INCLUDE 'greybox.inc' %]
[% INCLUDE 'doc-head-close.inc' %]
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.min.js"></script>
[% INCLUDE 'datatables-strings.inc' %]
<script type="text/javascript" src="[% themelang %]/js/datatables.js"></script>
<script type="text/javascript">
//<![CDATA[
function Import(Breeding,AuthType) {
opener.document.location="../authorities/authorities.pl?breedingid="+Breeding+"&authtypecode="+AuthType;
window.close();
return false;
}
function closemenu(){
$(".linktools").hide();
$("tr").removeClass("selected");
}
$(document).ready(function(){
$("#CheckAll").click(function(){
$(".checkboxed").checkCheckboxes();
return false;
});
$("#CheckNone").click(function(){
$(".checkboxed").unCheckCheckboxes();
return false;
});
$("#resultst").dataTable($.extend(true, {}, dataTablesDefaults, {
"sDom": 't',
"aoColumnDefs": [
{ "aTargets": [ -1,-2,-3 ], "bSortable": false, "bSearchable": false },
],
"aaSorting": [[ 1, "asc" ]],
"bPaginate": false
}));
/* Inline edit/delete links */
$("td").click(function(event){
var $tgt = $(event.target);
var row = $(this).parent();
$(".linktools").hide();
$("tr").removeClass("selected");
row.addClass("selected");
if($tgt.is("a")||$tgt.is(":nth-child(7)")||$tgt.is(":nth-child(8)")||$tgt.is(":nth-child(9)")||$tgt.is(":nth-child(10)")){
return true;
} else {
var position = $(this).offset();
var top = position.top+5;
var left = position.left+5;
$(".linktools",row).show().css("position","absolute").css("top",top).css("left",left);
}
});
$("form[name='f']").submit(function(){
if ($('input[type=checkbox]').filter(':checked').length == 0) {
alert(_("Please choose at least one Z39.50 target"));
return false;
} else
return true;
});
});
[% IF ( total_pages ) %]
function validate_goto_page(){
var page = $('#goto_page').val();
if(isNaN(page)) {
alert(_("The page entered is not a number."));
return false;
}
else if(page < 1 || page > [% total_pages %] ) {
alert(_("The page should be a number between 1 and ") + [% total_pages %] + ".");
return false;
}
else {
return true;
}
}
[% END %]
//]]>
</script>
<style type="text/css">
.linktools { background-color:#FFF;border-top:1px solid #DDD; border-left: 1px solid #DDD; border-right: 1px solid #666; border-bottom:1px solid #666;display: none; white-space: nowrap;}
.linktools a { font-size : 85%; text-decoration:none; padding:.3em;;background-color:#FFF; display:block;float:left;border-right:1px solid #DDD;}
.linktools a:hover { background-color:#EEE;color:#CC3300;border-right:1px solid #CCC;}
tr.selected { background-color : #FFFFCC; } tr.selected td { background-color : transparent; }
</style>
[% IF ( opsearch ) %]
<style type="text/css">
#custom-doc { width:53em;*width:51.72em;min-width:689px; margin:auto; text-align:left; }
</style>
</head>
<body id="cat_z3950_auth_search" class="cat">
<div id="custom-doc" class="yui-t7">
[% ELSE %]
</head>
<body style="padding:.5em;">
<div>
[% END %]
<div id="bd">
[% IF ( opsearch ) %]
<h2>Z39.50 Authority search points</h2>
<form method="post" action="z3950_auth_search.pl" name="f" class="checkboxed">
<input type="hidden" name="op" id="op" value="do_search" />
<div class="yui-g">
<div class="yui-u first">
<fieldset class="rows">
<ol>
<li><label for="srchany">Keyword (any): </label> <input type="text" id="srchany" name="srchany" value="" /></li>
<li><label for="nameany">Name (any): </label> <input type="text" id="nameany" name="nameany" value="" /></li>
<li><label for="authorany">Author (any): </label> <input type="text" id="authorany" name="authorany" value="" /></li>
<li><label for="authorpersonal">Author (personal): </label> <input type="text" id="authorpersonal" name="authorpersonal" value="" /></li>
<li><label for="authorcorp">Author (corporate): </label> <input type="text" id="authorcorp" name="authorcorp" value="" /></li>
<li><label for="authormeetingcon">Author (meeting/conference): </label> <input type="text" id="authormeetingcon" name="authormeetingcon" value="" /></li>
</ol>
</fieldset>
</div>
<div class="yui-u">
<fieldset class="rows">
<ol>
<li><label for="subject">Subject heading: </label> <input type="text" id="subject" name="subject" value="" /></li>
<li><label for="subjectsubdiv">Subject sub-division: </label> <input type="text" id="subjectsubdiv" name="subjectsubdiv" value="" /></li>
<li><label for="title">Title (any): </label> <input type="text" id="title" name="title" value="[% title |html %]" /></li>
<li><label for="uniformtitle">Title (uniform): </label> <input type="text" id="uniformtitle" name="uniformtitle" value="[% uniformtitle |html %]" /></li>
</ol>
</fieldset>
</div>
</div>
<div class="yui-g">
<h2>Search targets <span style="display: inline; font-size: 70%; padding-left: 1em;"><span class="checkall"><a id="CheckAll" href="#">Select all</a></span><span class="clearall"><a id="CheckNone" href="#">Clear all</a></span></span></h2>
[% FOREACH serverloo IN serverloop %]
<p>
[% IF ( serverloo.checked ) %]
<input type="checkbox" name="id" id="z3950_[% serverloo.id %]" value="[% serverloo.id %]" checked="checked" />
[% ELSE %]
<input type="checkbox" name="id" id="z3950_[% serverloo.id %]" value="[% serverloo.id %]" />
[% END %]
<label for="z3950_[% serverloo.id %]">[% serverloo.name %]</label>
</p>
[% END %]
</div>
<fieldset class="action"><input type="submit" class="submit" value="Search" onclick="cursor :'wait'"/> <a class="cancel close" href="#">Cancel</a></fieldset>
</form>
[% ELSE %]
<h2>Results for Authority Records</h2>
[% IF ( breeding_loop ) %]
<table id="resultst">
<thead> <tr>
<th>Server</th>
<th>Heading</th>
<th>Authority Type</th>
<th>MARC</th>
<!-- <th>Card</th> -->
<th>&nbsp;</th>
</tr></thead>
<tbody>[% FOREACH breeding_loo IN breeding_loop %]
[% IF ( breeding_loo.breedingid ) %]
<tr id="row[% breeding_loo.breedingid %]">
<td>[% breeding_loo.server %] <div class="linktools"><a href="/cgi-bin/koha/catalogue/showmarc.pl?importid=[% breeding_loo.breedingid %]" rel="gb_page_center[600,500]">Preview MARC</a> <a href="#" onclick="Import([% breeding_loo.breedingid %],'[% breeding_loo.heading_code %]'); return false">Import</a><a href="#" onclick="closemenu();return false;" title="Close this menu"> X </a></div> </td>
<td>[% breeding_loo.heading %]</td>
<td>[% breeding_loo.heading_code %]</td>
<td><a href="/cgi-bin/koha/catalogue/showmarc.pl?importid=[% breeding_loo.breedingid %]" title="MARC" rel="gb_page_center[600,500]">MARC</a></td>
<!-- <td><a href="/cgi-bin/koha/catalogue/showmarc.pl?viewas=card&amp;importid=[% breeding_loo.breedingid %]" title="MARC" rel="gb_page_center[600,500]">Card</a></td> -->
<td><a href="#" onclick="Import([% breeding_loo.breedingid %],'[% breeding_loo.heading_code %]'); return false">Import</a></td>
</tr>
[% END %]
[% END %]</tbody>
</table>
<form method="post" action="z3950_auth_search.pl" id="page_form" name="page_form" class="checkboxed">
<input type="hidden" name="op" id="op" value="do_search" />
<input type="hidden" name="current_page" id="current_page" value="[% current_page %]" />
<input type="hidden" id="nameany" name="nameany" value="[% nameany %]" />
<input type="hidden" id="authorany" name="authorany" value="[% authorany %]" />
<input type="hidden" id="authorcorp" name="authorcorp" value="[% authorcorp %]" />
<input type="hidden" id="authorpersonal" name="authorpersonal" value="[% authorpersonal %]" />
<input type="hidden" id="authormeetingcon" name="authormeetingcon" value="[% authormeetingcon %]" />
<input type="hidden" id="title" name="title" value="[% title %]" />
<input type="hidden" id="uniformtitle" name="uniformtitle" value="[% uniformtitle %]" />
<input type="hidden" id="subject" name="subject" value="[% subject %]" />
<input type="hidden" id="subjectsubdiv" name="subjectsubdiv" value="[% subjectsubdiv %]" />
<input type="hidden" id="heading" name="heading" value="[% heading %]" />
<input type="hidden" id="srchany" name="srchany" value="[% srchany %]" />
[% FOREACH server IN servers %]
<input type="hidden" name="id" id="z3950_[% server.id %]" value="[% server.id %]" />
[% END %]
[% IF ( show_prevbutton ) %]
<input type="button" name="changepage_prev" value="Previous Page" onclick="$('#current_page').val([% current_page %]-1);$('#page_form').submit();" />
[% END %]
Page [% current_page %] / [% total_pages %]
[% IF ( show_nextbutton ) %]
<input type="button" name="changepage_next" value="Next Page" onclick="$('#current_page').val([% current_page %]+1);$('#page_form').submit();" />
[% END %]
<br />Go to page : <input id="goto_page" name="goto_page" value="[% current_page %]" size="4" /><input type="submit" name="changepage_goto" onclick="return validate_goto_page();" value="Go" />
</form>
<p><form method="get" action="/cgi-bin/koha/cataloguing/z3950_auth_search.pl"><input type="submit" value="Try Another Search"/></form></p>
[% ELSE %]
[% IF ( errconn ) %]
<div class="dialog alert">
<ul>
[% FOREACH errcon IN errconn %]
[% IF ( errcon.error == '10000' ) %]<li>Connection failed to [% errcon.server %]</li>
[% ELSIF ( errcon.error == '10007' ) %]<li>Connection timeout to [% errcon.server %]</li>[% END %]
[% END %]
</ul>
</div>
[% END %]
<div class="dialog message">Nothing found.</div>
<p><form method="get" action="/cgi-bin/koha/cataloguing/z3950_auth_search.pl"><input type="submit" value="Try Another Search"/></form></p>
[% END %]
[% END %]
</div>
</div>
[% IF ( numberpending ) %]<h3 align="center">Still [% numberpending %] servers to search</h3>[% END %]
</body>
</html>
Loading…
Cancel
Save