Bug 19040: Refactor GetMarcBiblio parameters
[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/;
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 XML::LibXML;
36 use XML::LibXSLT;
37 use YAML::Syck qw( LoadFile );
38 use CGI qw/:standard -oldstyle_urls/;
39 use C4::Context;
40 use C4::Biblio;
41
42
43 =head1 NAME
44
45 Koha::OAI::Server::Repository - Handles OAI-PMH requests for a Koha database.
46
47 =head1 SYNOPSIS
48
49   use Koha::OAI::Server::Repository;
50
51   my $repository = Koha::OAI::Server::Repository->new();
52
53 =head1 DESCRIPTION
54
55 This object extend HTTP::OAI::Repository object.
56 It accepts OAI-PMH HTTP requests and returns result.
57
58 This OAI-PMH server can operate in a simple mode and extended one.
59
60 In simple mode, repository configuration comes entirely from Koha system
61 preferences (OAI-PMH:archiveID and OAI-PMH:MaxCount) and the server returns
62 records in marcxml or dublin core format. Dublin core records are created from
63 koha marcxml records transformed with XSLT. Used XSL file is located in koha-
64 tmpl/intranet-tmpl/prog/en/xslt directory and chosen based on marcflavour,
65 respecively MARC21slim2OAIDC.xsl for MARC21 and  MARC21slim2OAIDC.xsl for
66 UNIMARC.
67
68 In extended mode, it's possible to parameter other format than marcxml or
69 Dublin Core. A new syspref OAI-PMH:ConfFile specify a YAML configuration file
70 which list available metadata formats and XSL file used to create them from
71 marcxml records. If this syspref isn't set, Koha OAI server works in simple
72 mode. A configuration file koha-oai.conf can look like that:
73
74   ---
75   format:
76     vs:
77       metadataPrefix: vs
78       metadataNamespace: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs
79       schema: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs.xsd
80       xsl_file: /usr/local/koha/xslt/vs.xsl
81     marc21:
82       metadataPrefix: marc21
83       metadataNamespace: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim
84       schema: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd
85       include_items: 1
86     marcxml:
87       metadataPrefix: marxml
88       metadataNamespace: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim
89       schema: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd
90       include_items: 1
91     oai_dc:
92       metadataPrefix: oai_dc
93       metadataNamespace: http://www.openarchives.org/OAI/2.0/oai_dc/
94       schema: http://www.openarchives.org/OAI/2.0/oai_dc.xsd
95       xsl_file: /usr/local/koha/koha-tmpl/intranet-tmpl/xslt/UNIMARCslim2OAIDC.xsl
96
97 Note the 'include_items' parameter which is the only mean to return item-level info.
98
99 =cut
100
101
102 sub new {
103     my ($class, %args) = @_;
104     my $self = $class->SUPER::new(%args);
105
106     $self->{ koha_identifier      } = C4::Context->preference("OAI-PMH:archiveID");
107     $self->{ koha_max_count       } = C4::Context->preference("OAI-PMH:MaxCount");
108     $self->{ koha_metadata_format } = ['oai_dc', 'marc21', 'marcxml'];
109     $self->{ koha_stylesheet      } = { }; # Build when needed
110
111     # Load configuration file if defined in OAI-PMH:ConfFile syspref
112     if ( my $file = C4::Context->preference("OAI-PMH:ConfFile") ) {
113         $self->{ conf } = LoadFile( $file );
114         my @formats = keys %{ $self->{conf}->{format} };
115         $self->{ koha_metadata_format } =  \@formats;
116     }
117
118     # OAI-PMH handles dates in UTC, so do that on the database level to avoid need for
119     # any conversions
120     C4::Context->dbh->prepare("SET time_zone='+00:00'")->execute();
121
122     # Check for grammatical errors in the request
123     my @errs = validate_request( CGI::Vars() );
124
125     # Is metadataPrefix supported by the repository?
126     my $mdp = param('metadataPrefix') || '';
127     if ( $mdp && !grep { $_ eq $mdp } @{$self->{ koha_metadata_format }} ) {
128         push @errs, new HTTP::OAI::Error(
129             code    => 'cannotDisseminateFormat',
130             message => "Dissemination as '$mdp' is not supported",
131         );
132     }
133
134     my $response;
135     if ( @errs ) {
136         $response = HTTP::OAI::Response->new(
137             requestURL  => self_url(),
138             errors      => \@errs,
139         );
140     }
141     else {
142         my %attr = CGI::Vars();
143         my $verb = delete $attr{verb};
144         my $class = "Koha::OAI::Server::$verb";
145         $response = $class->new($self, %attr);
146     }
147
148     $response->set_handler( XML::SAX::Writer->new( Output => *STDOUT ) );
149     $response->xslt( "/opac-tmpl/xslt/OAI.xslt" );
150     $response->generate;
151
152     bless $self, $class;
153     return $self;
154 }
155
156
157 sub get_biblio_marcxml {
158     my ($self, $biblionumber, $format) = @_;
159     my $with_items = 0;
160     if ( my $conf = $self->{conf} ) {
161         $with_items = $conf->{format}->{$format}->{include_items};
162     }
163     my $record = GetMarcBiblio({
164         biblionumber => $biblionumber,
165         embed_items  => $with_items,
166         opac         => 1 });
167     $record ? $record->as_xml_record() : undef;
168 }
169
170
171 sub stylesheet {
172     my ( $self, $format ) = @_;
173
174     my $stylesheet = $self->{ koha_stylesheet }->{ $format };
175     unless ( $stylesheet ) {
176         my $xsl_file = $self->{ conf }
177                        ? $self->{ conf }->{ format }->{ $format }->{ xsl_file }
178                        : ( C4::Context->config('intrahtdocs') .
179                          '/prog/en/xslt/' .
180                          C4::Context->preference('marcflavour') .
181                          'slim2OAIDC.xsl' );
182         $xsl_file || die( "No stylesheet found for $format" );
183         my $parser = XML::LibXML->new();
184         my $xslt = XML::LibXSLT->new();
185         my $style_doc = $parser->parse_file( $xsl_file );
186         $stylesheet = $xslt->parse_stylesheet( $style_doc );
187         $self->{ koha_stylesheet }->{ $format } = $stylesheet;
188     }
189
190     return $stylesheet;
191 }
192
193
194 sub items_included {
195     my ( $self, $format ) = @_;
196
197     if ( my $conf = $self->{ conf } ) {
198         return $conf->{ format }->{ $format }->{ include_items };
199     }
200     return 0;
201 }
202
203 1;