#!/usr/bin/perl
use strict;
+use warnings;
+use diagnostics;
+use CGI qw/:standard -oldstyle_urls/;
+use vars qw( $GZIP );
use C4::Context;
-use C4::Biblio;
-=head1 OAI-PMH for koha
-This file is an implementation of the OAI-PMH protocol for koha. Its purpose
-is to share metadata in Dublin core format with harvester like PKP-Harverster.
-Presently, all the bibliographic records managed by the runing koha instance
-are publicly shared (as the opac is).
+BEGIN {
+ eval { require PerlIO::gzip };
+ $GZIP = ($@) ? 0 : 1;
+}
-=head1 Package MARC::Record::KOHADC
+unless ( C4::Context->preference('OAI-PMH') ) {
+ print
+ header(
+ -type => 'text/plain; charset=utf-8',
+ -charset => 'utf-8',
+ -status => '404 OAI-PMH service is disabled',
+ ),
+ "OAI-PMH service is disabled";
+ exit;
+}
-This package is a sub-class of the MARC::File::USMARC. It add methods and functions
-to map the content of a marc record (of any flavor) to Dublin core.
-As soon as it is possible, mapping between marc fields and there semantic
-are got from ::GetMarcFromKohaField fonction from C4::Biblio (see also the "Koha
-to MARC mapping" preferences).
+my @encodings = http('HTTP_ACCEPT_ENCODING');
+if ( $GZIP && grep { defined($_) && $_ eq 'gzip' } @encodings ) {
+ print header(
+ -type => 'text/xml; charset=utf-8',
+ -charset => 'utf-8',
+ -Content-Encoding => 'gzip',
+ );
+ binmode( STDOUT, ":gzip" );
+}
+else {
+ print header(
+ -type => 'text/xml; charset=utf-8',
+ -charset => 'utf-8',
+ );
+}
-=cut
+binmode( STDOUT, ":utf8" );
+my $repository = C4::OAI::Repository->new();
-package MARC::Record::KOHADC;
-use vars ('@ISA');
-@ISA = qw(MARC::Record);
+# __END__ Main Prog
-use MARC::File::USMARC;
-sub new { # Get a MAR::Record as parameter and bless it as MARC::Record::KOHADC
- shift;
- bless shift;
-}
+#
+# Extends HTTP::OAI::ResumptionToken
+# A token is identified by:
+# - metadataPrefix
+# - from
+# - until
+# - offset
+#
+package C4::OAI::ResumptionToken;
-sub subfield {
- my $self = shift;
- my ($t,$sf) = @_;
+use strict;
+use warnings;
+use diagnostics;
+use HTTP::OAI;
- return $self->SUPER::subfield( @_ ) unless wantarray;
+use base ("HTTP::OAI::ResumptionToken");
- my @field = $self->field($t);
- my @list = ();
- my $f;
- foreach $f ( @field ) {
- push( @list, $f->subfield( $sf ) );
- }
- return @list;
-}
+sub new {
+ my ($class, %args) = @_;
-sub getfields {
-my $marc = shift;
-my @result = ();
+ my $self = $class->SUPER::new(%args);
- foreach my $kohafield ( @_ ) {
- my ( $field, $subfield ) = ::GetMarcFromKohaField( $kohafield, '' );
- push( @result, $field < 10 ? $marc->field( $field )->as_string() : $marc->subfield( $field, $subfield ) );
+ my ($metadata_prefix, $offset, $from, $until);
+ if ( $args{ resumptionToken } ) {
+ ($metadata_prefix, $offset, $from, $until)
+ = split( ':', $args{resumptionToken} );
+ }
+ else {
+ $metadata_prefix = $args{ metadataPrefix };
+ $from = $args{ from } || '1970-01-01';
+ $until = $args{ until };
+ unless ( $until) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime( time );
+ $until = sprintf( "%.4d-%.2d-%.2d", $year+1900, $mon+1,$mday );
}
-# @result>1 ? \@result : $result[0];
- \@result;
-}
+ $offset = $args{ offset } || 0;
+ }
-sub Status {
- my $self = shift;
- undef;
-}
+ $self->{ metadata_prefix } = $metadata_prefix;
+ $self->{ offset } = $offset;
+ $self->{ from } = $from;
+ $self->{ until } = $until;
-sub Title {
- my $self = shift;
- $self->getfields('biblio.title');
-}
+ $self->resumptionToken(
+ join( ':', $metadata_prefix, $offset, $from, $until ) );
+ $self->cursor( $offset );
-sub Creator {
- my $self = shift;
- $self->getfields('biblio.author');
+ return $self;
}
-sub Subject {
- my $self = shift;
- $self->getfields('bibliosubject.subject');
-}
+# __END__ C4::OAI::ResumptionToken
-sub DateStamp {
- my $self = shift;
- my ($d,$h) = split( ' ', $self->{'biblio.timestamp'} );
- $d . "T" . $h . "Z";
-}
-sub Date {
- my $self = shift;
- my ($str) = @{$self->getfields('biblioitems.publicationyear')};
- my ($y,$m,$d) = (substr($str,0,4), substr($str,4,2), substr($str,6,2));
- $y=1970 unless($y>0); $m=1 unless($m>0); $d=1 unless($d>0);
+package C4::OAI::Identify;
- sprintf( "%.4d-%.2d-%.2d", $y,$m,$d);
-}
+use strict;
+use warnings;
+use diagnostics;
+use HTTP::OAI;
+use C4::Context;
-sub Description {
- my $self = shift;
- undef;
-}
+use base ("HTTP::OAI::Identify");
-sub Identifier {
- my $self = shift;
- my $id = $self->getfields('biblio.biblionumber')->[0];
-
-# get url of this script and assume that OAI server is in the same place as opac-detail script
-# and build a direct link to the record.
- my $uri = $ENV{'SCRIPT_URI'};
- $uri= "http://" . $ENV{'HTTP_HOST'} . $ENV{'REQUEST_URI'} unless( $uri ); # SCRIPT_URI doesn't exist on all httpd server
- $uri =~ s#[^/]+$##;
- [
- C4::Context->preference("OAI-PMH:archiveID") .":" .$id,
- "${uri}opac-detail.pl?bib=$id",
- @{$self->getfields('biblioitems.isbn', 'biblioitems.issn')}
- ];
-}
+sub new {
+ my ($class, $repository) = @_;
-sub Language {
- my $self = shift;
- undef;
-}
+ my ($baseURL) = $repository->self_url() =~ /(.*)\?.*/;
+ my $self = $class->SUPER::new(
+ baseURL => $baseURL,
+ repositoryName => C4::Context->preference("LibraryName"),
+ adminEmail => C4::Context->preference("KohaAdminEmailAddress"),
+ MaxCount => C4::Context->preference("OAI-PMH:MaxCount"),
+ granularity => 'YYYY-MM-DD',
+ earliestDatestamp => '0001-01-01',
+ deletedRecord => 'no',
+ );
-sub Type {
- my $self = shift;
- $self->getfields('biblioitems.itemtype');
-}
+ # FIXME - alas, the description element is not so simple; to validate
+ # against the OAI-PMH schema, it cannot contain just a string,
+ # but one or more elements that validate against another XML schema.
+ # For now, simply omitting it.
+ # $self->description( "Koha OAI Repository" );
-sub Publisher {
- my $self = shift;
- $self->getfields('biblioitems.publishercode');
-}
+ $self->compression( 'gzip' );
-sub Set {
-my $set = &OAI::KOHA::Set();
- [ map( $_=$_->[0], @$set) ];
+ return $self;
}
-=head1 The OAI::KOHA package
+# __END__ C4::OAI::Identify
-This package is a subclass of the OAI::DC data provider. It overides needed methods
-and provide the links between the OAI-PMH request and the koha application.
-The data used in answers are from the koha table I<bibio>.
-=cut
-package OAI::KOHA;
+package C4::OAI::ListMetadataFormats;
-use C4::OAI::DC;
-use vars ('@ISA');
-@ISA = ("C4::OAI::DC");
+use strict;
+use warnings;
+use diagnostics;
+use HTTP::OAI;
-=head2 Set
+use base ("HTTP::OAI::ListMetadataFormats");
-return the Set list to the I<verb=ListSets> query. Data are from the 'OAI-PMH:Set' preference.
+sub new {
+ my ($class, $repository) = @_;
-=cut
+ my $self = $class->SUPER::new();
-sub Set {
-# [
-# ['BRISE','Experimental unimarc set for BRISE network'],
-# ['BRISE:EMSE','EMSE set in BRISE network']
-# ];
-#
-# A blinder correctement
- [ map( $_ = [ split(",", $_)], split( "\n",C4::Context->preference("OAI-PMH:Set") ) ) ];
+ if ( $repository->{ conf } ) {
+ foreach my $name ( @{ $repository->{ koha_metadata_format } } ) {
+ my $format = $repository->{ conf }->{ format }->{ $name };
+ $self->metadataFormat( HTTP::OAI::MetadataFormat->new(
+ metadataPrefix => $format->{metadataPrefix},
+ schema => $format->{schema},
+ metadataNamespace => $format->{metadataNamespace}, ) );
+ }
+ }
+ else {
+ $self->metadataFormat( HTTP::OAI::MetadataFormat->new(
+ metadataPrefix => 'oai_dc',
+ schema => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
+ metadataNamespace => 'http://www.openarchives.org/OAI/2.0/oai_dc/'
+ ) );
+ $self->metadataFormat( HTTP::OAI::MetadataFormat->new(
+ metadataPrefix => 'marcxml',
+ schema => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim.xsd',
+ metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim'
+ ) );
+ }
+
+ return $self;
}
-=head2 new
+# __END__ C4::OAI::ListMetadataFormats
-The new method is the constructor for this class. It doesn't have any parameters and
-get required data from koha preferences. Koha I<LibraryName> is used to identify the
-OAI-PMH repository, I<OAI-PMH:MaxCount> is used to set the maximun number of records
-returned at the same time in answers to I<verb=ListRecords> or I<verb=ListIdentifiers>
-queries.
-The method return a blessed reference.
-=cut
+package C4::OAI::Record;
-# constructor
-sub new
-{
- my $classname = shift;
- my $self = $classname->SUPER::new ();
+use strict;
+use warnings;
+use diagnostics;
+use HTTP::OAI;
+use HTTP::OAI::Metadata::OAI_DC;
- # set configuration
- $self->{'repositoryName'} = C4::Context->preference("LibraryName");
- $self->{'MaxCount'} = C4::Context->preference("OAI-PMH:MaxCount");
- $self->{'adminEmail'} = C4::Context->preference("KohaAdminEmailAddress");
+use base ("HTTP::OAI::Record");
- bless $self, $classname;
- return $self;
-}
+sub new {
+ my ($class, $repository, $marcxml, $timestamp, %args) = @_;
-=head2 dispose
+ my $self = $class->SUPER::new(%args);
-The dispose method is used as a destructor. It call just the SUPER::dispose method.
+ $timestamp =~ s/ /T/, $timestamp .= 'Z';
+ $self->header( new HTTP::OAI::Header(
+ identifier => $args{identifier},
+ datestamp => $timestamp,
+ ) );
-=cut
+ my $parser = XML::LibXML->new();
+ my $record_dom = $parser->parse_string( $marcxml );
+ my $format = $args{metadataPrefix};
+ if ( $format ne 'marcxml' ) {
+ $record_dom = $repository->stylesheet($format)->transform( $record_dom );
+ }
+ $self->metadata( HTTP::OAI::Metadata->new( dom => $record_dom ) );
-# destructor
-sub dispose
-{
- my ($self) = @_;
- $self->SUPER::dispose ();
+ return $self;
}
-# now date
-sub now {
-my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime( time );
+# __END__ C4::OAI::Record
- sprintf( "%.4d-%.2d-%.2d", $year+1900, $mon+1,$mday );
-}
-# build the resumptionTocken fom ($metadataPrefix,$offset,$from,$until)
-=head2 buildResumptionToken and parseResumptionToken
+package C4::OAI::GetRecord;
-Theses two functions are used to manage resumption tokens. The choosed syntax is simple as
-possible, a token is only the metadata prefix, the offset in the full answer, the from and
-the until date (in the yyyy-mm-dd format) joined by ':' caracter.
+use strict;
+use warnings;
+use diagnostics;
+use HTTP::OAI;
+
+use base ("HTTP::OAI::GetRecord");
+
+
+sub new {
+ my ($class, $repository, %args) = @_;
+
+ my $self = HTTP::OAI::GetRecord->new(%args);
+
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("
+ SELECT marcxml, timestamp
+ FROM biblioitems
+ WHERE biblionumber=? " );
+ my $prefix = $repository->{koha_identifier} . ':';
+ my ($biblionumber) = $args{identifier} =~ /^$prefix(.*)/;
+ $sth->execute( $biblionumber );
+ my ($marcxml, $timestamp);
+ unless ( ($marcxml, $timestamp) = $sth->fetchrow ) {
+ return HTTP::OAI::Response->new(
+ requestURL => $repository->self_url(),
+ errors => [ new HTTP::OAI::Error(
+ code => 'idDoesNotExist',
+ message => "There is no biblio record with this identifier",
+ ) ] ,
+ );
+ }
-I<buildResumptionToken> get the four elements as parameters and return the ':' separated
-string.
+ #$self->header( HTTP::OAI::Header->new( identifier => $args{identifier} ) );
+ $self->record( C4::OAI::Record->new(
+ $repository, $marcxml, $timestamp, %args ) );
-I<parseResumptionToken> is used to set the default values to the from and until date, the
-metadata prefix using the resumption tocken if necessary. This function have four parameters
-(from,until,metadata prefix and resumption tocken) which can be undefined and return every
-time this list of values correctly set. The missing values are set with defaults: offset=0,
-from= 1970-01-01 and until is set to current date.
+ return $self;
+}
-=cut
+# __END__ C4::OAI::GetRecord
-sub buildResumptionToken {
- join( ':', @_ );
-}
-# parse the resumptionTocken
-sub parseResumptionToken {
-my ($from, $until, $metadataPrefix, $resumptionToken) = @_;
-my $offset = 0;
- if( $resumptionToken ) {
- ($metadataPrefix,$offset,$from,$until) = split( ':', $resumptionToken );
- }
+package C4::OAI::ListIdentifiers;
- $from = "1970-01-01" unless( $from );
- $until = &now unless( $until );
- ($metadataPrefix, $offset, $from, $until );
+use strict;
+use warnings;
+use diagnostics;
+use HTTP::OAI;
+
+use base ("HTTP::OAI::ListIdentifiers");
+
+
+sub new {
+ my ($class, $repository, %args) = @_;
+
+ my $self = HTTP::OAI::ListIdentifiers->new(%args);
+
+ my $token = new C4::OAI::ResumptionToken( %args );
+ my $dbh = C4::Context->dbh;
+ my $sql = "SELECT biblionumber, timestamp
+ FROM biblioitems
+ WHERE timestamp >= ? AND timestamp <= ?
+ LIMIT " . $repository->{koha_max_count} . "
+ OFFSET " . $token->{offset};
+ my $sth = $dbh->prepare( $sql );
+ $sth->execute( $token->{from}, $token->{until} );
+
+ my $pos = $token->{offset};
+ while ( my ($biblionumber, $timestamp) = $sth->fetchrow ) {
+ $timestamp =~ s/ /T/, $timestamp .= 'Z';
+ $self->identifier( new HTTP::OAI::Header(
+ identifier => $repository->{ koha_identifier} . ':' . $biblionumber,
+ datestamp => $timestamp,
+ ) );
+ $pos++;
+ }
+ $self->resumptionToken( new C4::OAI::ResumptionToken(
+ metadataPrefix => $token->{metadata_prefix},
+ from => $token->{from},
+ until => $token->{until},
+ offset => $pos ) ) if ($pos > $token->{offset});
+
+ return $self;
}
-=head2 Archive_ListSets
+# __END__ C4::OAI::ListIdentifiers
-return the full list Set to the I<verb=ListSets> query. Data are from the 'OAI-PMH:Set' preference.
-=cut
-# get full list of sets from the archive
-sub Archive_ListSets
-{
- &Set();
+package C4::OAI::ListRecords;
+
+use strict;
+use warnings;
+use diagnostics;
+use HTTP::OAI;
+
+use base ("HTTP::OAI::ListRecords");
+
+
+sub new {
+ my ($class, $repository, %args) = @_;
+
+ my $self = HTTP::OAI::ListRecords->new(%args);
+
+ my $token = new C4::OAI::ResumptionToken( %args );
+ my $dbh = C4::Context->dbh;
+ my $sql = "SELECT biblionumber, marcxml, timestamp
+ FROM biblioitems
+ WHERE timestamp >= ? AND timestamp <= ?
+ LIMIT " . $repository->{koha_max_count} . "
+ OFFSET " . $token->{offset};
+ my $sth = $dbh->prepare( $sql );
+ $sth->execute( $token->{from}, $token->{until} );
+
+ my $pos = $token->{offset};
+ while ( my ($biblionumber, $marcxml, $timestamp) = $sth->fetchrow ) {
+ $self->record( C4::OAI::Record->new(
+ $repository, $marcxml, $timestamp,
+ identifier => $repository->{ koha_identifier } . ':' . $biblionumber,
+ metadataPrefix => $token->{metadata_prefix}
+ ) );
+ $pos++;
+ }
+ $self->resumptionToken( new C4::OAI::ResumptionToken(
+ metadataPrefix => $token->{metadata_prefix},
+ from => $token->{from},
+ until => $token->{until},
+ offset => $pos ) ) if ($pos > $token->{offset});
+
+ return $self;
}
-
-=head2 Archive_GetRecord
-This method select the record specified as its first parameter from the koha I<biblio>
-table and return a reference to a MARC::Record::KOHADC object.
+# __END__ C4::OAI::ListRecords
-=cut
-# get a single record from the archive
-sub Archive_GetRecord
-{
- my ($self, $identifier, $metadataFormat) = @_;
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare("SELECT biblionumber,timestamp FROM biblio WHERE biblionumber=?");
- my $prefixID = C4::Context->preference("OAI-PMH:archiveID"); $prefixID=qr{$prefixID:};
- $identifier =~ s/^$prefixID//;
+package C4::OAI::Repository;
- $sth->execute( $identifier );
+use base ("HTTP::OAI::Repository");
- if( my $r = $sth->fetchrow_hashref() ) {
- my $marc = new MARC::Record::KOHADC( ::GetMarcBiblio( $identifier ) );
- $marc->{'biblio.timestamp'} = $r->{'timestamp'};
- return $marc ;
- }
+use strict;
+use warnings;
+use diagnostics;
- $self->AddError ('idDoesNotExist', 'The value of the identifier argument is unknown or illegal in this repository');
- undef;
-}
+use HTTP::OAI;
+use HTTP::OAI::Repository qw/:validate/;
-=head2 Archive_ListRecords
+use XML::SAX::Writer;
+use XML::LibXML;
+use XML::LibXSLT;
+use YAML::Syck qw( LoadFile );
+use CGI qw/:standard -oldstyle_urls/;
-This method return a list of 'MaxCount' references to MARC::Record::KOHADC object build from the
-koha I<biblio> table according to its parameters : set, from and until date, metadata prefix
-and resumption token.
+use C4::Context;
+use C4::Biblio;
-=cut
-# list metadata records from the archive
-sub Archive_ListRecords
-{
- my ($self, $set, $from, $until, $metadataPrefix, $resumptionToken) = @_;
-
- my @allrows = ();
- my $marc;
- my $offset;
- my $tokenInfo;
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare("SELECT biblionumber,timestamp FROM biblio WHERE DATE(timestamp) >= ? and DATE(timestamp) <= ? LIMIT ? OFFSET ?");
- my $count;
-
- ($metadataPrefix, $offset, $from, $until ) = &parseResumptionToken($from, $until, $metadataPrefix, $resumptionToken);
-
-warn( "Archive_ListRecords : $set, $from, $until, $metadataPrefix, $resumptionToken\n");
- $sth->execute( $from,$until,$self->{'MaxCount'}?$self->{'MaxCount'}:100000, $offset );
-
- while( my $r = $sth->fetchrow_hashref() ) {
- my $marc = new MARC::Record::KOHADC( ::GetMarcBiblio( $r->{'biblionumber'} ) );
- $marc->{'biblio.timestamp'} = $r->{'timestamp'};
- push( @allrows, $marc );
- }
-
- $sth = $dbh->prepare("SELECT count(*) FROM biblioitems WHERE DATE(timestamp) >= ? and DATE(timestamp) <= ?");
- $sth->execute($from, $until);
- ( $count ) = $sth->fetchrow_array();
-
- unless( @allrows ) {
- $self->AddError ('noRecordsMatch', 'The combination of the values of arguments results in an empty set');
- }
-
- if( $offset + $self->{'MaxCount'} < $count ) { # Not at the end
- $offset = $offset + $self->{'MaxCount'};
- $resumptionToken = &buildResumptionToken($metadataPrefix,$offset,$from,$until);
- $tokenInfo = { 'completeListSize' => $count, 'cursor' => $offset };
- }
- else {
- $resumptionToken = '';
- $tokenInfo = {};
- }
- ( \@allrows, $resumptionToken, $metadataPrefix, $tokenInfo );
-}
+sub new {
+ my ($class, %args) = @_;
+ my $self = $class->SUPER::new(%args);
-package main;
+ $self->{ koha_identifier } = C4::Context->preference("OAI-PMH:archiveID");
+ $self->{ koha_max_count } = C4::Context->preference("OAI-PMH:MaxCount");
+ $self->{ koha_metadata_format } = ['oai_dc', 'marcxml'];
+ $self->{ koha_stylesheet } = { }; # Build when needed
-=head1 Main package
+ # Load configuration file if defined in OAI-PMH:ConfFile syspref
+ if ( my $file = C4::Context->preference("OAI-PMH:ConfFile") ) {
+ $self->{ conf } = LoadFile( $file );
+ my @formats = keys %{ $self->{conf}->{format} };
+ $self->{ koha_metadata_format } = \@formats;
+ }
-The I<main> function is the starting point of the service. The first step is
-to verify if the service is enable using the 'OAI-PMH' preference value
-(See Koha systeme preferences).
+ # Check for grammatical errors in the request
+ my @errs = validate_request( CGI::Vars() );
-If the service is enable, it create a new instance of the OAI::KOHA data
-provider (see before) and run the service.
+ # Is metadataPrefix supported by the respository?
+ my $mdp = param('metadataPrefix') || '';
+ if ( $mdp && !grep { $_ eq $mdp } @{$self->{ koha_metadata_format }} ) {
+ push @errs, new HTTP::OAI::Error(
+ code => 'cannotDisseminateFormat',
+ message => "Dissemination as '$mdp' is not supported",
+ );
+ }
-=cut
+ my $response;
+ if ( @errs ) {
+ $response = HTTP::OAI::Response->new(
+ requestURL => self_url(),
+ errors => \@errs,
+ );
+ }
+ else {
+ my %attr = CGI::Vars();
+ my $verb = delete( $attr{verb} );
+ if ( grep { $_ eq $verb } qw( ListSets ) ) {
+ $response = HTTP::OAI::Response->new(
+ requestURL => $self->self_url(),
+ errors => [ new HTTP::OAI::Error(
+ code => 'noSetHierarchy',
+ message => "Koha repository doesn't have sets",
+ ) ] ,
+ );
+ }
+ elsif ( $verb eq 'Identify' ) {
+ $response = C4::OAI::Identify->new( $self );
+ }
+ elsif ( $verb eq 'ListMetadataFormats' ) {
+ $response = C4::OAI::ListMetadataFormats->new( $self );
+ }
+ elsif ( $verb eq 'GetRecord' ) {
+ $response = C4::OAI::GetRecord->new( $self, %attr );
+ }
+ elsif ( $verb eq 'ListRecords' ) {
+ $response = C4::OAI::ListRecords->new( $self, %attr );
+ }
+ elsif ( $verb eq 'ListIdentifiers' ) {
+ $response = C4::OAI::ListIdentifiers->new( $self, %attr );
+ }
+ }
-sub disable {
- print "Status:404 OAI-PMH service is disabled\n";
- print "Content-type: text/plain\n\n";
+ $response->set_handler( XML::SAX::Writer->new( Output => *STDOUT ) );
+ $response->generate;
- print "OAI-PMH service is disable.\n";
+ bless $self, $class;
+ return $self;
}
-sub main
-{
- return &disable() unless( C4::Context->preference('OAI-PMH') );
- my $OAI = new OAI::KOHA();
- $OAI->Run;
- $OAI->dispose;
+sub stylesheet {
+ my ( $self, $format ) = @_;
+
+ my $stylesheet = $self->{ koha_stylesheet }->{ $format };
+ unless ( $stylesheet ) {
+ my $xsl_file = $self->{ conf }
+ ? $self->{ conf }->{ format }->{ $format }->{ xsl_file }
+ : ( C4::Context->config('intranetdir') .
+ "/koha-tmpl/intranet-tmpl/prog/en/xslt/" .
+ C4::Context->preference('marcflavour') .
+ "slim2OAIDC.xsl" );
+ my $parser = XML::LibXML->new();
+ my $xslt = XML::LibXSLT->new();
+ my $style_doc = $parser->parse_file( $xsl_file );
+ $stylesheet = $xslt->parse_stylesheet( $style_doc );
+ $self->{ koha_stylesheet }->{ $format } = $stylesheet;
+ }
+
+ return $stylesheet;
}
-main;
-1;
+
+=head1 NAME
+
+C4::OAI::Repository - Handles OAI-PMH requests for a Koha database.
+
+=head1 SYNOPSIS
+
+ use C4::OAI::Repository;
+
+ my $repository = C4::OAI::Repository->new();
+
+=head1 DESCRIPTION
+
+This object extend HTTP::OAI::Repository object.
+It accepts OAI-PMH HTTP requests and returns result.
+
+This OAI-PMH server can operate in a simple mode and extended one.
+
+In simple mode, repository configuration comes entirely from Koha system
+preferences (OAI-PMH:archiveID and OAI-PMH:MaxCount) and the server returns
+records in marcxml or dublin core format. Dublin core records are created from
+koha marcxml records tranformed with XSLT. Used XSL file is located in
+koha-tmpl/intranet-tmpl/prog/en/xslt directory and choosed based on marcflavour,
+respecively MARC21slim2OAIDC.xsl for MARC21 and MARC21slim2OAIDC.xsl for
+UNIMARC.
+
+In extende mode, it's possible to parameter other format than marcxml or Dublin
+Core. A new syspref OAI-PMH:ConfFile specify a YAML configuration file which
+list available metadata formats and XSL file used to create them from marcxml
+records. If this syspref isn't set, Koha OAI server works in simple mode. A
+configuration file koha-oai.conf can look like that:
+
+ ---
+ format:
+ vs:
+ metadataPrefix: vs
+ metadataNamespace: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs
+ schema: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs.xsd
+ xsl_file: /usr/local/koha/xslt/vs.xsl
+ marcxml:
+ metadataPrefix: marxml
+ metadataNamespace: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim
+ schema: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd
+ oai_dc:
+ metadataPrefix: oai_dc
+ metadataNamespace: http://www.openarchives.org/OAI/2.0/oai_dc/
+ schema: http://www.openarchives.org/OAI/2.0/oai_dc.xsd
+ xsl_file: /usr/local/koha/koha-tmpl/intranet-tmpl/xslt/UNIMARCslim2OAIDC.xsl
+
+=cut
+
+
+