Bug 17600: Standardize our EXPORT_OK
[koha.git] / Koha / OAI / Server / Repository.pm
1 # Copyright Tamil s.a.r.l. 2008-2015
2 # Copyright Biblibre 2008-2015
3 # Copyright The National Library of Finland, University of Helsinki 2016
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 package Koha::OAI::Server::Repository;
21
22 use Modern::Perl;
23 use HTTP::OAI;
24 use HTTP::OAI::Repository qw( validate_request );
25
26 use base ("HTTP::OAI::Repository");
27
28 use Koha::OAI::Server::Identify;
29 use Koha::OAI::Server::ListSets;
30 use Koha::OAI::Server::ListMetadataFormats;
31 use Koha::OAI::Server::GetRecord;
32 use Koha::OAI::Server::ListRecords;
33 use Koha::OAI::Server::ListIdentifiers;
34 use XML::SAX::Writer;
35 use YAML::XS;
36 use CGI qw/:standard -oldstyle_urls/;
37 use C4::Context;
38 use C4::Biblio qw( GetMarcBiblio );
39 use Koha::XSLT::Base;
40
41 =head1 NAME
42
43 Koha::OAI::Server::Repository - Handles OAI-PMH requests for a Koha database.
44
45 =head1 SYNOPSIS
46
47   use Koha::OAI::Server::Repository;
48
49   my $repository = Koha::OAI::Server::Repository->new();
50
51 =head1 DESCRIPTION
52
53 This object extend HTTP::OAI::Repository object.
54 It accepts OAI-PMH HTTP requests and returns result.
55
56 This OAI-PMH server can operate in a simple mode and extended one.
57
58 In simple mode, repository configuration comes entirely from Koha system
59 preferences (OAI-PMH:archiveID and OAI-PMH:MaxCount) and the server returns
60 records in marcxml or dublin core format. Dublin core records are created from
61 koha marcxml records transformed with XSLT. Used XSL file is located in koha-
62 tmpl/intranet-tmpl/prog/en/xslt directory and chosen based on marcflavour,
63 respecively MARC21slim2OAIDC.xsl for MARC21 and  MARC21slim2OAIDC.xsl for
64 UNIMARC.
65
66 In extended mode, it's possible to parameter other format than marcxml or
67 Dublin Core. A new syspref OAI-PMH:ConfFile specify a YAML configuration file
68 which list available metadata formats and XSL file used to create them from
69 marcxml records. If this syspref isn't set, Koha OAI server works in simple
70 mode. A configuration file koha-oai.conf can look like that:
71
72   ---
73   format:
74     vs:
75       metadataPrefix: vs
76       metadataNamespace: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs
77       schema: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs.xsd
78       xsl_file: /usr/local/koha/xslt/vs.xsl
79     marc21:
80       metadataPrefix: marc21
81       metadataNamespace: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim
82       schema: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd
83       include_items: 1
84     marcxml:
85       metadataPrefix: marxml
86       metadataNamespace: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim
87       schema: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd
88       include_items: 1
89     oai_dc:
90       metadataPrefix: oai_dc
91       metadataNamespace: http://www.openarchives.org/OAI/2.0/oai_dc/
92       schema: http://www.openarchives.org/OAI/2.0/oai_dc.xsd
93       xsl_file: /usr/local/koha/koha-tmpl/intranet-tmpl/xslt/UNIMARCslim2OAIDC.xsl
94
95 Note the 'include_items' parameter which is the only mean to return item-level info.
96
97 =cut
98
99
100 sub new {
101     my ($class, %args) = @_;
102     my $self = $class->SUPER::new(%args);
103
104     $self->{ koha_identifier      } = C4::Context->preference("OAI-PMH:archiveID");
105     $self->{ koha_max_count       } = C4::Context->preference("OAI-PMH:MaxCount");
106     $self->{ koha_metadata_format } = ['oai_dc', 'marc21', 'marcxml'];
107     $self->{ xslt_engine          } = Koha::XSLT::Base->new;
108
109     # Load configuration file if defined in OAI-PMH:ConfFile syspref
110     if ( my $file = C4::Context->preference("OAI-PMH:ConfFile") ) {
111         $self->{ conf } = YAML::XS::LoadFile( $file );
112         my @formats = keys %{ $self->{conf}->{format} };
113         $self->{ koha_metadata_format } =  \@formats;
114     }
115
116     # OAI-PMH handles dates in UTC, so do that on the database level to avoid need for
117     # any conversions
118     my $sth = C4::Context->dbh->prepare('SELECT @@session.time_zone');
119     $sth->execute();
120     my ( $orig_tz ) = $sth->fetchrow();
121     $self->{ mysql_orig_tz } = $orig_tz;
122     C4::Context->dbh->prepare("SET time_zone='+00:00'")->execute();
123
124     # Check for grammatical errors in the request
125     my @errs = validate_request( CGI::Vars() );
126
127     # Is metadataPrefix supported by the repository?
128     my $mdp = param('metadataPrefix') || '';
129     if ( $mdp && !grep { $_ eq $mdp } @{$self->{ koha_metadata_format }} ) {
130         push @errs, HTTP::OAI::Error->new(
131             code    => 'cannotDisseminateFormat',
132             message => "Dissemination as '$mdp' is not supported",
133         );
134     }
135
136     my $response;
137     if ( @errs ) {
138         $response = HTTP::OAI::Response->new(
139             requestURL  => self_url(),
140             errors      => \@errs,
141         );
142     }
143     else {
144         my %attr = CGI::Vars();
145         my $verb = delete $attr{verb};
146         my $class = "Koha::OAI::Server::$verb";
147         $response = $class->new($self, %attr);
148     }
149
150     $response->set_handler( XML::SAX::Writer->new( Output => *STDOUT ) );
151     $response->xslt( "/opac-tmpl/xslt/OAI.xslt" );
152     $response->generate;
153
154     bless $self, $class;
155     return $self;
156 }
157
158
159 sub DESTROY {
160     my ( $self ) = @_;
161
162     # Reset time zone to the original value
163     C4::Context->dbh->prepare("SET time_zone='" . $self->{ mysql_orig_tz } . "'")->execute()
164         if $self->{ mysql_orig_tz };
165 }
166
167
168 sub get_biblio_marcxml {
169     my ($self, $biblionumber, $format) = @_;
170     my $with_items = 0;
171     if ( my $conf = $self->{conf} ) {
172         $with_items = $conf->{format}->{$format}->{include_items};
173     }
174     my $record = GetMarcBiblio({
175         biblionumber => $biblionumber,
176         embed_items  => $with_items,
177         opac         => 1 });
178     $record ? $record->as_xml_record() : undef;
179 }
180
181
182 sub stylesheet {
183     my ( $self, $format ) = @_;
184     my $xsl_file = $self->{ conf }
185         ? $self->{ conf }->{ format }->{ $format }->{ xsl_file }
186         : ( C4::Context->config('intrahtdocs') .
187             '/prog/en/xslt/' .
188             C4::Context->preference('marcflavour') .
189             'slim2OAIDC.xsl'
190     );
191     return $xsl_file;
192 }
193
194
195 sub items_included {
196     my ( $self, $format ) = @_;
197
198     if ( my $conf = $self->{ conf } ) {
199         return $conf->{ format }->{ $format }->{ include_items };
200     }
201     return 0;
202 }
203
204 1;