Bug 26265: (QA follow-up) Remove g option from regex, add few dirs
[koha-equinox.git] / opac / oai.pl
index ab56d79..f6a7786 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 
-# Copyright Biblibre 2008
+# Copyright Tamil s.a.r.l. 2008-2015
+# Copyright Biblibre 2008-2015
 #
 # This file is part of Koha.
 #
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 
-use strict;
-use warnings;
-
+use Modern::Perl;
 use CGI qw( :standard -oldstyle_urls -utf8 );
 use vars qw( $GZIP );
 use C4::Context;
+use Koha::OAI::Server::Repository;
 
 
 BEGIN {
@@ -59,698 +59,5 @@ else {
 }
 
 binmode STDOUT, ':encoding(UTF-8)';
-my $repository = C4::OAI::Repository->new();
-
-# __END__ Main Prog
-
-
-#
-# Extends HTTP::OAI::ResumptionToken
-# A token is identified by:
-# - metadataPrefix
-# - from
-# - until
-# - offset
-#
-package C4::OAI::ResumptionToken;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-
-use base ("HTTP::OAI::ResumptionToken");
-
-
-sub new {
-    my ($class, %args) = @_;
-
-    my $self = $class->SUPER::new(%args);
-
-    my ($metadata_prefix, $offset, $from, $until, $set);
-    if ( $args{ resumptionToken } ) {
-        ($metadata_prefix, $offset, $from, $until, $set)
-            = 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 );
-        }
-        #Add times to the arguments, when necessary, so they correctly match against the DB timestamps
-        $from .= 'T00:00:00Z' if length($from) == 10;
-        $until .= 'T23:59:59Z' if length($until) == 10;
-        $offset = $args{ offset } || 0;
-        $set = $args{set};
-    }
-
-    $self->{ metadata_prefix } = $metadata_prefix;
-    $self->{ offset          } = $offset;
-    $self->{ from            } = $from;
-    $self->{ until           } = $until;
-    $self->{ set             } = $set;
-    $self->{ from_arg        } = _strip_UTC_designators($from);
-    $self->{ until_arg       } = _strip_UTC_designators($until);
-
-    $self->resumptionToken(
-        join( '/', $metadata_prefix, $offset, $from, $until, $set ) );
-    $self->cursor( $offset );
-
-    return $self;
-}
-
-sub _strip_UTC_designators {
-    my ( $timestamp ) = @_;
-    $timestamp =~ s/T/ /g;
-    $timestamp =~ s/Z//g;
-    return $timestamp;
-}
-
-# __END__ C4::OAI::ResumptionToken
-
-
-
-package C4::OAI::Identify;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-use C4::Context;
-
-use base ("HTTP::OAI::Identify");
-
-sub new {
-    my ($class, $repository) = @_;
-
-    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       => C4::Context->preference("OAI-PMH:DeletedRecord") || 'no',
-    );
-
-    # 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" );
-
-    $self->compression( 'gzip' );
-
-    return $self;
-}
-
-# __END__ C4::OAI::Identify
-
-
-
-package C4::OAI::ListMetadataFormats;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-
-use base ("HTTP::OAI::ListMetadataFormats");
-
-sub new {
-    my ($class, $repository) = @_;
-
-    my $self = $class->SUPER::new();
-
-    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;
-}
-
-# __END__ C4::OAI::ListMetadataFormats
-
-
-
-package C4::OAI::Record;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-use HTTP::OAI::Metadata::OAI_DC;
-
-use base ("HTTP::OAI::Record");
-
-sub new {
-    my ($class, $repository, $marcxml, $timestamp, $setSpecs, %args) = @_;
-
-    my $self = $class->SUPER::new(%args);
-
-    $timestamp =~ s/ /T/, $timestamp .= 'Z';
-    $self->header( new HTTP::OAI::Header(
-        identifier  => $args{identifier},
-        datestamp   => $timestamp,
-    ) );
-
-    foreach my $setSpec (@$setSpecs) {
-        $self->header->setSpec($setSpec);
-    }
-
-    my $parser = XML::LibXML->new();
-    my $record_dom = $parser->parse_string( $marcxml );
-    my $format =  $args{metadataPrefix};
-    if ( $format ne 'marcxml' ) {
-        my %args = (
-            OPACBaseURL => "'" . C4::Context->preference('OPACBaseURL') . "'"
-        );
-        $record_dom = $repository->stylesheet($format)->transform($record_dom, %args);
-    }
-    $self->metadata( HTTP::OAI::Metadata->new( dom => $record_dom ) );
-
-    return $self;
-}
-
-# __END__ C4::OAI::Record
-
-package C4::OAI::DeletedRecord;
-
-use Modern::Perl;
-use HTTP::OAI;
-use HTTP::OAI::Metadata::OAI_DC;
-
-use base ("HTTP::OAI::Record");
-
-sub new {
-    my ($class, $timestamp, $setSpecs, %args) = @_;
-
-    my $self = $class->SUPER::new(%args);
-
-    $timestamp =~ s/ /T/, $timestamp .= 'Z';
-    $self->header( new HTTP::OAI::Header(
-        status      => 'deleted',
-        identifier  => $args{identifier},
-        datestamp   => $timestamp,
-    ) );
-
-    foreach my $setSpec (@$setSpecs) {
-        $self->header->setSpec($setSpec);
-    }
-
-    return $self;
-}
-
-# __END__ C4::OAI::DeletedRecord
-
-
-
-package C4::OAI::GetRecord;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-use C4::OAI::Sets;
-
-use base ("HTTP::OAI::GetRecord");
-
-
-sub new {
-    my ($class, $repository, %args) = @_;
-
-    my $self = HTTP::OAI::GetRecord->new(%args);
-
-    my $dbh = C4::Context->dbh;
-    my $prefix = $repository->{koha_identifier} . ':';
-    my ($biblionumber) = $args{identifier} =~ /^$prefix(.*)/;
-    my ($marcxml, $timestamp);
-    my $deleted = 0;
-    unless ( ($marcxml, $timestamp) = $dbh->selectrow_array(q/
-        SELECT marcxml, timestamp
-        FROM   biblioitems
-        WHERE  biblionumber=? /, undef, $biblionumber)) {
-
-      unless ( ($marcxml, $timestamp) = $dbh->selectrow_array(q/
-          SELECT biblionumber, timestamp
-          FROM deletedbiblio
-          WHERE biblionumber=? /, undef, $biblionumber )) {
-
-
-        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",
-                ) ] ,
-        );
-      } else {
-        $deleted = 1;
-      }
-    }
-    my $oai_sets = GetOAISetsBiblio($biblionumber);
-    my @setSpecs;
-    foreach (@$oai_sets) {
-        push @setSpecs, $_->{spec};
-    }
-
-    #$self->header( HTTP::OAI::Header->new( identifier  => $args{identifier} ) );
-    ($deleted == 1) ? $self->record( C4::OAI::DeletedRecord->new(
-        $timestamp, \@setSpecs, %args ) )
-        : $self->record( C4::OAI::Record->new(
-        $repository, $marcxml, $timestamp, \@setSpecs, %args ) );
-    return $self;
-}
-
-# __END__ C4::OAI::GetRecord
-
-
-
-package C4::OAI::ListIdentifiers;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-use C4::OAI::Sets;
-
-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 $set;
-    if(defined $token->{'set'}) {
-        $set = GetOAISetBySpec($token->{'set'});
-    }
-    my $max = $repository->{koha_max_count};
-    my $sql = "
-        (SELECT biblioitems.biblionumber, biblioitems.timestamp
-        FROM biblioitems
-    ";
-    $sql .= " JOIN oai_sets_biblios ON biblioitems.biblionumber = oai_sets_biblios.biblionumber " if defined $set;
-    $sql .= " WHERE timestamp >= ? AND timestamp <= ? ";
-    $sql .= " AND oai_sets_biblios.set_id = ? " if defined $set;
-    $sql .= ") UNION
-        (SELECT deletedbiblio.biblionumber, timestamp FROM deletedbiblio";
-    $sql .= " JOIN oai_sets_biblios ON deletedbiblio.biblionumber = oai_sets_biblios.biblionumber " if defined $set;
-    $sql .= " WHERE DATE(timestamp) >= ? AND DATE(timestamp) <= ? ";
-    $sql .= " AND oai_sets_biblios.set_id = ? " if defined $set;
-
-    $sql .= ") ORDER BY biblionumber
-        LIMIT " . ($max+1) . "
-        OFFSET $token->{offset}
-    ";
-    my $sth = $dbh->prepare( $sql );
-    my @bind_params = ($token->{'from_arg'}, $token->{'until_arg'});
-    push @bind_params, $set->{'id'} if defined $set;
-    push @bind_params, ($token->{'from'}, $token->{'until'});
-    push @bind_params, $set->{'id'} if defined $set;
-    $sth->execute( @bind_params );
-
-    my $count = 0;
-    while ( my ($biblionumber, $timestamp) = $sth->fetchrow ) {
-        $count++;
-        if ( $count > $max ) {
-            $self->resumptionToken(
-                new C4::OAI::ResumptionToken(
-                    metadataPrefix  => $token->{metadata_prefix},
-                    from            => $token->{from},
-                    until           => $token->{until},
-                    offset          => $token->{offset} + $max,
-                    set             => $token->{set}
-                )
-            );
-            last;
-        }
-        $timestamp =~ s/ /T/, $timestamp .= 'Z';
-        $self->identifier( new HTTP::OAI::Header(
-            identifier => $repository->{ koha_identifier} . ':' . $biblionumber,
-            datestamp  => $timestamp,
-        ) );
-    }
-
-    return $self;
-}
-
-# __END__ C4::OAI::ListIdentifiers
-
-package C4::OAI::Description;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-use HTTP::OAI::SAXHandler qw/ :SAX /;
-
-sub new {
-    my ( $class, %args ) = @_;
-
-    my $self = {};
-
-    if(my $setDescription = $args{setDescription}) {
-        $self->{setDescription} = $setDescription;
-    }
-    if(my $handler = $args{handler}) {
-        $self->{handler} = $handler;
-    }
-
-    bless $self, $class;
-    return $self;
-}
-
-sub set_handler {
-    my ( $self, $handler ) = @_;
-
-    $self->{handler} = $handler if $handler;
-
-    return $self;
-}
-
-sub generate {
-    my ( $self ) = @_;
-
-    g_data_element($self->{handler}, 'http://www.openarchives.org/OAI/2.0/', 'setDescription', {}, $self->{setDescription});
-
-    return $self;
-}
-
-# __END__ C4::OAI::Description
-
-package C4::OAI::ListSets;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-use C4::OAI::Sets;
-
-use base ("HTTP::OAI::ListSets");
-
-sub new {
-    my ( $class, $repository, %args ) = @_;
-
-    my $self = HTTP::OAI::ListSets->new(%args);
-
-    my $token = C4::OAI::ResumptionToken->new(%args);
-    my $sets = GetOAISets;
-    my $pos = 0;
-    foreach my $set (@$sets) {
-        if ($pos < $token->{offset}) {
-            $pos++;
-            next;
-        }
-        my @descriptions;
-        foreach my $desc (@{$set->{'descriptions'}}) {
-            push @descriptions, C4::OAI::Description->new(
-                setDescription => $desc,
-            );
-        }
-        $self->set(
-            HTTP::OAI::Set->new(
-                setSpec => $set->{'spec'},
-                setName => $set->{'name'},
-                setDescription => \@descriptions,
-            )
-        );
-        $pos++;
-        last if ($pos + 1 - $token->{offset}) > $repository->{koha_max_count};
-    }
-
-    $self->resumptionToken(
-        new C4::OAI::ResumptionToken(
-            metadataPrefix => $token->{metadata_prefix},
-            offset         => $pos
-        )
-    ) if ( $pos > $token->{offset} );
-
-    return $self;
-}
-
-# __END__ C4::OAI::ListSets;
-
-package C4::OAI::ListRecords;
-
-use strict;
-use warnings;
-use HTTP::OAI;
-use C4::OAI::Sets;
-
-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 $set;
-    if(defined $token->{'set'}) {
-        $set = GetOAISetBySpec($token->{'set'});
-    }
-    my $max = $repository->{koha_max_count};
-    my $sql = "
-        (SELECT biblioitems.biblionumber, biblioitems.marcxml, biblioitems.timestamp
-        FROM biblioitems
-    ";
-    $sql .= " JOIN oai_sets_biblios ON biblioitems.biblionumber = oai_sets_biblios.biblionumber " if defined $set;
-    $sql .= " WHERE timestamp >= ? AND timestamp <= ? ";
-    $sql .= " AND oai_sets_biblios.set_id = ? " if defined $set;
-    $sql .= ") UNION
-        (SELECT deletedbiblio.biblionumber, null as marcxml, timestamp FROM deletedbiblio";
-    $sql .= " JOIN oai_sets_biblios ON deletedbiblio.biblionumber = oai_sets_biblios.biblionumber " if defined $set;
-    $sql .= " WHERE DATE(timestamp) >= ? AND DATE(timestamp) <= ? ";
-    $sql .= " AND oai_sets_biblios.set_id = ? " if defined $set;
-
-    $sql .= ") ORDER BY biblionumber
-        LIMIT " . ($max + 1) . "
-        OFFSET $token->{offset}
-    ";
-    my $sth = $dbh->prepare( $sql );
-    my @bind_params = ($token->{'from_arg'}, $token->{'until_arg'});
-    push @bind_params, $set->{'id'} if defined $set;
-    push @bind_params, ($token->{'from'}, $token->{'until'});
-    push @bind_params, $set->{'id'} if defined $set;
-    $sth->execute( @bind_params );
-
-    my $count = 0;
-    while ( my ($biblionumber, $marcxml, $timestamp) = $sth->fetchrow ) {
-        $count++;
-        if ( $count > $max ) {
-            $self->resumptionToken(
-                new C4::OAI::ResumptionToken(
-                    metadataPrefix  => $token->{metadata_prefix},
-                    from            => $token->{from},
-                    until           => $token->{until},
-                    offset          => $token->{offset} + $max,
-                    set             => $token->{set}
-                )
-            );
-            last;
-        }
-        my $oai_sets = GetOAISetsBiblio($biblionumber);
-        my @setSpecs;
-        foreach (@$oai_sets) {
-            push @setSpecs, $_->{spec};
-        }
-        if ($marcxml) {
-          $self->record( C4::OAI::Record->new(
-              $repository, $marcxml, $timestamp, \@setSpecs,
-              identifier      => $repository->{ koha_identifier } . ':' . $biblionumber,
-              metadataPrefix  => $token->{metadata_prefix}
-          ) );
-        } else {
-          $self->record( C4::OAI::DeletedRecord->new(
-          $timestamp, \@setSpecs, identifier => $repository->{ koha_identifier } . ':' . $biblionumber ) );
-        }
-    }
-
-    return $self;
-}
-
-# __END__ C4::OAI::ListRecords
-
-
-
-package C4::OAI::Repository;
-
-use base ("HTTP::OAI::Repository");
-
-use strict;
-use warnings;
-
-use HTTP::OAI;
-use HTTP::OAI::Repository qw/:validate/;
-
-use XML::SAX::Writer;
-use XML::LibXML;
-use XML::LibXSLT;
-use YAML::Syck qw( LoadFile );
-use CGI qw/:standard -oldstyle_urls/;
-
-use C4::Context;
-use C4::Biblio;
-
-
-sub new {
-    my ($class, %args) = @_;
-    my $self = $class->SUPER::new(%args);
-
-    $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
-
-    # 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;
-    }
-
-    # Check for grammatical errors in the request
-    my @errs = validate_request( CGI::Vars() );
-
-    # 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",
-        );
-    }
-
-    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 ( $verb eq 'ListSets' ) {
-            $response = C4::OAI::ListSets->new($self, %attr);
-        }
-        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 );
-        }
-    }
-
-    $response->set_handler( XML::SAX::Writer->new( Output => *STDOUT ) );
-    $response->generate;
-
-    bless $self, $class;
-    return $self;
-}
-
-
-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('intrahtdocs') .
-                         '/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;
-}
-
-
-
-=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
-
-
 
+my $repository = Koha::OAI::Server::Repository->new();