fixing html output (server side now)
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / WWW / SuperCat / Feed.pm
1 package OpenILS::WWW::SuperCat::Feed;
2 use strict; use warnings;
3 use vars qw/$parser/;
4 use OpenSRF::EX qw(:try);
5 use XML::LibXML;
6 use XML::LibXSLT;
7 use OpenSRF::Utils::SettingsClient;
8 use CGI;
9
10 sub new {
11         my $class = shift;
12         my $type = shift;
13         if ($type) {
14                 $class .= '::'.$type;
15                 return $class->new;
16         }
17         throw OpenSRF::EX::ERROR ("I need a feed type!") ;
18 }
19
20 sub build {
21         my $class = shift;
22         my $xml = shift;
23
24         $parser = new XML::LibXML if (!$parser);
25
26         my $self = { doc => $parser->parse_string($xml), items => [] };
27
28         return bless $self => $class;
29 }
30
31 sub type {
32         my $self = shift;
33         my $type = shift;
34         $self->{type} = $type if ($type);
35         return $self->{type};
36 }
37
38 sub base {
39         my $self = shift;
40         my $base = shift;
41         $self->{base} = $base if ($base);
42         return $self->{base};
43 }
44
45 sub root {
46         my $self = shift;
47         my $root = shift;
48         $self->{root} = $root if ($root);
49         return $self->{root};
50 }
51
52 sub unapi {
53         my $self = shift;
54         my $unapi = shift;
55         $self->{unapi} = $unapi if ($unapi);
56         return $self->{unapi};
57 }
58
59 sub push_item {
60         my $self = shift;
61         push @{ $self->{items} }, @_;
62 }
63
64 sub items {
65         my $self = shift;
66         return @{ $self->{items} } if (wantarray);
67         return $self->{items};
68 }
69
70 sub _add_node {
71         my $self = shift;
72
73         my $xpath = shift;
74         my $new = shift;
75
76         for my $node ($self->{doc}->findnodes($xpath)) {
77                 $node->appendChild($new);
78                 last;
79         }
80 }
81
82 sub _create_node {
83         my $self = shift;
84
85         my $xpath = shift;
86         my $ns = shift;
87         my $name = shift;
88         my $text = shift;
89         my $attrs = shift;
90
91         for my $node ($self->{doc}->findnodes($xpath)) {
92                 my $new = $self->{doc}->createElement($name) if (!$ns);
93                 $new = $self->{doc}->createElementNS($ns,$name) if ($ns);
94
95                 $new->appendChild( $self->{doc}->createTextNode( $text ) )
96                         if (defined $text);
97
98                 if (ref($attrs)) {
99                         for my $key (keys %$attrs) {
100                                 $new->setAttribute( $key => $$attrs{$key} );
101                         }
102                 }
103
104                 $node->appendChild( $new );
105
106                 return $new;
107         }
108 }
109
110 sub add_item {
111         my $self = shift;
112         my $class = ref($self) || $self;
113         $class .= '::item';
114
115         my $item_xml = shift;
116         my $entry = $class->new($item_xml);
117
118         $entry->base($self->base);
119         $entry->unapi($self->unapi);
120
121         $self->push_item($entry);
122         return $entry;
123 }
124
125 sub composeDoc {
126         my $self = shift;
127         for my $root ( $self->{doc}->findnodes($self->{item_xpath}) ) {
128                 for my $item ( $self->items ) {
129                         $root->appendChild( $item->{doc}->documentElement );
130                 }
131                 last;
132         }
133 }
134
135 sub toString {
136         my $self = shift;
137         $self->composeDoc;
138         return $self->{doc}->toString(1);
139 }
140
141 sub id {};
142 sub link {};
143 sub title {};
144 sub update_ts {};
145 sub creator {};
146
147 #----------------------------------------------------------
148
149 package OpenILS::WWW::SuperCat::Feed::atom;
150 use base 'OpenILS::WWW::SuperCat::Feed';
151
152 sub new {
153         my $class = shift;
154         my $self = $class->SUPER::build('<atom:feed xmlns:atom="http://www.w3.org/2005/Atom"/>');
155         $self->{type} = 'application/xml';
156         $self->{item_xpath} = '/atom:feed';
157         return $self;
158 }
159
160 sub title {
161         my $self = shift;
162         my $text = shift;
163         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:title', $text);
164 }
165
166 sub update_ts {
167         my $self = shift;
168         my $text = shift;
169         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:updated', $text);
170 }
171
172 sub creator {
173         my $self = shift;
174         my $text = shift;
175         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:author');
176         $self->_create_node('/atom:feed/atom:author', 'http://www.w3.org/2005/Atom','atom:name', $text);
177 }
178
179 sub link {
180         my $self = shift;
181         my $type = shift;
182         my $id = shift;
183         my $mime = shift || "application/$type+xml";
184
185         $type = 'self' if ($type eq 'atom');
186
187         $self->_create_node(
188                 $self->{item_xpath},
189                 'http://www.w3.org/2005/Atom',
190                 'atom:link',
191                 undef,
192                 { rel => $type,
193                   href => $id,
194                   type => $mime,
195                 }
196         );
197 }
198
199 sub id {
200         my $self = shift;
201         my $id = shift;
202
203         $self->_create_node( '/atom:feed', 'http://www.w3.org/2005/Atom', 'atom:id', $id );
204 }
205
206 package OpenILS::WWW::SuperCat::Feed::atom::item;
207 use base 'OpenILS::WWW::SuperCat::Feed::atom';
208
209 sub new {
210         my $class = shift;
211         my $xml = shift;
212         my $self = $class->SUPER::build($xml);
213         $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom');
214         $self->{item_xpath} = '/atom:entry';
215         $self->{type} = 'application/xml';
216         return $self;
217 }
218
219
220 #----------------------------------------------------------
221
222 package OpenILS::WWW::SuperCat::Feed::rss2;
223 use base 'OpenILS::WWW::SuperCat::Feed';
224
225 sub new {
226         my $class = shift;
227         my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
228         $self->{type} = 'application/xml';
229         $self->{item_xpath} = '/rss/channel';
230         return $self;
231 }
232
233 sub title {
234         my $self = shift;
235         my $text = shift;
236         $self->_create_node('/rss/channel',undef,'title', $text);
237 }
238
239 sub update_ts {
240         my $self = shift;
241         my $text = shift;
242         $self->_create_node('/rss/channel',undef,'lastBuildDate', $text);
243 }
244
245 sub creator {
246         my $self = shift;
247         my $text = shift;
248         $self->_create_node('/rss/channel', undef,'generator', $text);
249 }
250
251 sub link {
252         my $self = shift;
253         my $type = shift;
254         my $id = shift;
255         my $mime = shift || "application/$type+xml";
256
257         $type = 'self' if ($type eq 'rss2');
258
259         $self->_create_node(
260                 $self->{item_xpath},
261                 undef,
262                 'link',
263                 $id,
264                 { rel => $type,
265                   type => $mime,
266                 }
267         );
268 }
269
270 package OpenILS::WWW::SuperCat::Feed::rss2::item;
271 use base 'OpenILS::WWW::SuperCat::Feed::rss2';
272
273 sub new {
274         my $class = shift;
275         my $xml = shift;
276         my $self = $class->SUPER::build($xml);
277         $self->{type} = 'application/xml';
278         $self->{item_xpath} = '/item';
279         return $self;
280 }
281
282
283 #----------------------------------------------------------
284
285 package OpenILS::WWW::SuperCat::Feed::mods;
286 use base 'OpenILS::WWW::SuperCat::Feed';
287
288 sub new {
289         my $class = shift;
290         my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/"/>');
291         $self->{type} = 'application/xml';
292         $self->{item_xpath} = '/mods:modsCollection';
293         return $self;
294 }
295
296 package OpenILS::WWW::SuperCat::Feed::mods::item;
297 use base 'OpenILS::WWW::SuperCat::Feed::mods';
298
299 sub new {
300         my $class = shift;
301         my $xml = shift;
302         my $self = $class->SUPER::build($xml);
303         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', 'mods');
304         $self->{type} = 'application/xml';
305         return $self;
306 }
307
308 my $linkid = 1;
309
310 sub link {
311         my $self = shift;
312         my $type = shift;
313         my $id = shift;
314
315         if ($type eq 'unapi' || $type eq 'opac') {
316                 $self->_create_node(
317                         'mods:mods',
318                         'http://www.loc.gov/mods/',
319                         'mods:relatedItem',
320                         undef,
321                         { type => 'otherFormat', id => 'link-'.$linkid }
322                 );
323                 $self->_create_node(
324                         "mods:mods/mods:relatedItem[\@id='link-$linkid']",
325                         'http://www.loc.gov/mods/',
326                         'mods:recordIdentifier',
327                         $id
328                 );
329                 $linkid++;
330         }
331 }
332
333
334 #----------------------------------------------------------
335
336 package OpenILS::WWW::SuperCat::Feed::marcxml;
337 use base 'OpenILS::WWW::SuperCat::Feed';
338
339 sub new {
340         my $class = shift;
341         my $self = $class->SUPER::build('<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim"/>');
342         $self->{type} = 'application/xml';
343         $self->{item_xpath} = '/marc:collection';
344         return $self;
345 }
346
347 package OpenILS::WWW::SuperCat::Feed::marcxml::item;
348 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
349
350 sub new {
351         my $class = shift;
352         my $xml = shift;
353         my $self = $class->SUPER::build($xml);
354         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/MARC21/slim', 'marc');
355         $self->{type} = 'application/xml';
356         return $self;
357 }
358
359 #----------------------------------------------------------
360
361 package OpenILS::WWW::SuperCat::Feed::html;
362 use base 'OpenILS::WWW::SuperCat::Feed::atom';
363
364 sub new {
365         my $class = shift;
366         my $self = $class->SUPER::new;
367         $self->{type} = 'application/xhtml+xml';
368         return $self;
369 }
370
371 our ($_parser, $_xslt, $atom2html_xslt);
372
373 sub toString {
374         my $self = shift;
375         my $base = $self->base;
376         my $root = $self->root;
377
378         $self->composeDoc;
379
380         $_parser ||= new XML::LibXML;
381         $_xslt ||= new XML::LibXSLT;
382
383         # parse the MODS xslt ...
384         $atom2html_xslt ||= $_xslt->parse_stylesheet(
385                 $_parser->parse_file(
386                         OpenSRF::Utils::SettingsClient
387                                 ->new
388                                 ->config_value( dirs => 'xsl' ).
389                         "/ATOM2XHTML.xsl"
390                 )
391         );
392
393         my $new_doc = $atom2html_xslt->transform($self->{doc}, base_dir => "'$root'");
394         return $new_doc->toString(1); 
395         return $atom2html_xslt->output_string($new_doc);
396 }
397
398
399 package OpenILS::WWW::SuperCat::Feed::html::item;
400 use base 'OpenILS::WWW::SuperCat::Feed::atom::item';
401
402 1;