Bug 19532: (follow-up) aria-hidden attr on OPAC, and more
[koha.git] / opac / unapi
1 #!/usr/bin/perl
2
3 # Copyright 2008-2009 LibLime
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 use Modern::Perl;
21
22 =head1 NAME
23
24 unapi - implement unAPI for the OPAC
25
26 =head1 SYNOPSIS
27
28 Retrieve http://library.example.org/cgi-bin/koha/unapi?id=koha:biblionumber:123&format=oai_dc
29
30 =head1 DESCRIPTION
31
32 Implements unAPI <http://unapi.info>, a small HTTP API for retrieving structured
33 content from a web application.  The primary application of unAPI in Koha is to
34 allow tools such as Zotero to identify and grab bibliographic record metadata in
35 an XML format such as OAI DC, RSS2, MARCXML, or MODS.
36
37 =cut
38
39 use CGI qw ( -utf8 );
40 use C4::Context;
41 use C4::Biblio qw( GetXmlBiblio );
42 use Koha::XSLT::Base;
43
44 my $cgi = CGI->new();
45 binmode(STDOUT, ":encoding(UTF-8)"); #output as utf8
46
47 =head1 VARIABLES
48
49 =head2 $format_to_stylesheet_map
50
51 This hashref of hashrefs maps from a MARC flavour and unAPI format
52 to the stylesheet that should be used to transform the bib MARCXML
53 to the desired output format.  As new MARC XSLT stylesheets are added,
54 (particularly for UNIMARC), this map should be updated.  Of course,
55 if/when we add support for emitting a format that is not genreated
56 by a stylesheet, the structure of this variable will have to be changed.
57 At present, this doubles as the list of output formats supported by
58 this unAPI implementation.
59
60 =cut
61
62 my $format_to_stylesheet_map = {
63     'MARC21' => {
64         'marcxml'      => 'identity.xsl',
65         'marcxml-full' => 'identity.xsl',
66         'mods'         => 'MARC21slim2MODS.xsl',
67         'mods-full'    => 'MARC21slim2MODS.xsl',
68         'mods3'        => 'MARC21slim2MODS3-1.xsl',
69         'mods3-full'   => 'MARC21slim2MODS3-1.xsl',
70         'oai_dc'       => 'MARC21slim2OAIDC.xsl',
71         'rdfdc',       => 'MARC21slim2RDFDC.xsl',
72         'rss2'         => 'MARC21slim2RSS2.xsl',
73         'rss2-full'    => 'MARC21slim2RSS2.xsl',
74         'srw_dc'       => 'MARC21slim2SRWDC.xsl',
75     },
76     'UNIMARC' => {
77         'marcxml'      => 'identity.xsl',
78         'marcxml-full' => 'identity.xsl',
79         'oai_dc'       => 'UNIMARCslim2OAIDC.xsl',
80         'rdfdc',       => 'UNIMARCslim2RDFDC.xsl',
81         'srw_dc'       => 'UNIMARCslim2SRWDC.xsl',
82     },
83 };
84
85 =head2 $format_info
86
87 This hashref maps from unAPI output formats to the <format> elements
88 used to describe them in an unAPI format request.
89
90 =cut
91
92 my $format_info = {
93     'marcxml' => q(<format name="marcxml" type="application/xml" namespace_uri="http://www.loc.gov/MARC21/slim" docs="http://www.loc.gov/marcxml/" schema_location="http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"/>),
94     'marcxml-full' => q(<format name="marcxml-full" type="application/xml" namespace_uri="http://www.loc.gov/MARC21/slim" docs="http://www.loc.gov/marcxml/" schema_location="http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"/>),
95     'mods' => q(<format name="mods" type="application/xml" namespace_uri="http://www.loc.gov/mods/" docs="http://www.loc.gov/mods/" schema_location="http://www.loc.gov/standards/mods/mods.xsd"/>),
96     'mods-full' => q(<format name="mods-full" type="application/xml" namespace_uri="http://www.loc.gov/mods/" docs="http://www.loc.gov/mods/" schema_location="http://www.loc.gov/standards/mods/mods.xsd"/>),
97     'mods3' => q(<format name="mods3" type="application/xml" namespace_uri="http://www.loc.gov/mods/v3" docs="http://www.loc.gov/mods/" schema_location="http://www.loc.gov/standards/mods/v3/mods-3-1.xsd"/>),
98     'mods3-full' => q(<format name="mods3-full" type="application/xml" namespace_uri="http://www.loc.gov/mods/v3" docs="http://www.loc.gov/mods/" schema_location="http://www.loc.gov/standards/mods/v3/mods-3-1.xsd"/>),
99     'oai_dc' => q(<format name="oai_dc" type="application/xml" namespace_uri="http://www.openarchives.org/OAI/2.0/oai_dc/" schema_location="http://www.openarchives.org/OAI/2.0/oai_dc.xsd"/>),
100     'rdfdc' => q(<format name="rdfdc" type="application/xml" namespace_uri="http://purl.org/dc/elements/1.1/" schema_location="http://purl.org/dc/elements/1.1/"/>),
101     'rss2' => q(<format name="rss2" type="application/xml"/>),
102     'rss2-full' => q(<format name="rss2-full" type="application/xml"/>),
103     'srw_dc' => q(<format name="srw_dc" type="application/xml" namespace_uri="info:srw/schema/1/dc-schema" schema_location="http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd"/>),
104 };
105
106 my $id     = $cgi->param('id');
107 my $format = $cgi->param('format');
108
109 if (not defined $format) {
110     emit_formats($id, $format_to_stylesheet_map, $format_info, $cgi);
111 } elsif ($id) {
112
113     # koha:biblionumber:0152018484
114     if ($id =~ /koha:biblionumber:(\d+)/) {
115         my $biblionumber = $1;
116
117         my $content;
118
119         my $marcxml = GetXmlBiblio($biblionumber);
120         unless (defined $marcxml) {
121             # no bib, so 404
122             print $cgi->header( -status => '404 record not found');
123             exit 0;
124         }
125
126         my $xslt_file = get_xslt_file( $format, $format_to_stylesheet_map, $format_info );
127         unless( defined $xslt_file ) {
128             print $cgi->header( -status => '406 invalid format requested' );
129             exit 0;
130         }
131         my $xslt_engine = Koha::XSLT::Base->new;
132         $content = $xslt_engine->transform({
133             xml => $marcxml,
134             file => $xslt_file,
135         });
136
137         if( !defined $content || $xslt_engine->err ) {
138             print $cgi->header( -status => '500 internal error' );
139             exit 0;
140         }
141
142         print $cgi->header( -type =>'application/xml', -charset => 'UTF-8' );
143         print $content;
144     } else {
145         # ID is obviously wrong, so 404
146         print $cgi->header( -status => '404 record not found');
147         exit 0;
148     }
149 } else {
150     # supplied a format but no id - caller is doing it wrong
151     print $cgi->header( -status => '400 bad request - if you specify format, must specify id');
152     exit 0;
153 }
154
155 exit 0;
156
157 sub emit_formats {
158     my ($id, $format_to_stylesheet_map, $format_info, $cgi) = @_;
159
160     if (defined $id) {
161         print $cgi->header( -type =>'application/xml', -status => '300 multiple choices' );
162     } else {
163         print $cgi->header( -type =>'application/xml', -status => '200 Ok' );
164     }
165
166     print "<?xml version='1.0' encoding='utf-8'  ?>\n";
167     if (defined $id) {
168         print qq(<formats id="$id">\n);
169     } else {
170         print "<formats>\n";
171     }
172
173     my $marcflavour = uc(C4::Context->preference('marcflavour'));
174     foreach my $format (sort keys %{ $format_to_stylesheet_map->{$marcflavour} }) {
175         print $format_info->{$format}, "\n";
176     }
177     print "</formats>\n";
178     return;
179 }
180
181
182 sub get_xslt_file {
183     my ($format, $format_to_stylesheet_map, $format_info) = @_;
184     $format = lc $format;
185
186     my $marcflavour = uc(C4::Context->preference('marcflavour'));
187     return unless $format_to_stylesheet_map->{$marcflavour}->{$format};
188
189     my $xslt_file = C4::Context->config('intrahtdocs') .
190                     "/prog/en/xslt/" .
191                     $format_to_stylesheet_map->{$marcflavour}->{$format};
192
193     return $xslt_file;
194 }
195
196 =head1 AUTHOR
197
198 Koha Development Team <http://koha-community.org/>
199
200 Originally written by Joshua Ferraro <jmf@liblime.com>
201
202 Improved by Galen Charlton <galen.charlton@liblime.com>
203
204 =cut