]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm
1aa1f88d8acb5da049eea492e8d0c35483e49b55
[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         return undef unless $xml;
24
25         $parser = new XML::LibXML if (!$parser);
26
27         my $self = { doc => $parser->parse_string($xml), items => [] };
28
29         return bless $self => $class;
30 }
31
32 sub type {
33         my $self = shift;
34         my $type = shift;
35         $self->{type} = $type if ($type);
36         return $self->{type};
37 }
38
39 sub search {
40         my $self = shift;
41         my $search = shift;
42         $self->{search} = $search if ($search);
43         return $self->{search};
44 }
45
46 sub class {
47         my $self = shift;
48         my $search = shift;
49         $self->{class} = $search if ($search);
50         return $self->{class};
51 }
52
53 sub lib {
54         my $self = shift;
55         my $lib = shift;
56         $self->{lib} = $lib if ($lib);
57         return $self->{lib};
58 }
59
60 sub base {
61         my $self = shift;
62         my $base = shift;
63         $self->{base} = $base if ($base);
64         return $self->{base};
65 }
66
67 sub root {
68         my $self = shift;
69         my $root = shift;
70         $self->{root} = $root if ($root);
71         return $self->{root};
72 }
73
74 sub unapi {
75         my $self = shift;
76         my $unapi = shift;
77         $self->{unapi} = $unapi if ($unapi);
78         return $self->{unapi};
79 }
80
81 sub push_item {
82         my $self = shift;
83         push @{ $self->{items} }, @_;
84 }
85
86 sub items {
87         my $self = shift;
88         return @{ $self->{items} } if (wantarray);
89         return $self->{items};
90 }
91
92 sub _add_node {
93         my $self = shift;
94
95         my $xpath = shift;
96         my $new = shift;
97
98         for my $node ($self->{doc}->findnodes($xpath)) {
99                 $node->appendChild($new);
100                 last;
101         }
102 }
103
104 sub _create_node {
105         my $self = shift;
106
107         my $xpath = shift;
108         my $ns = shift;
109         my $name = shift;
110         my $text = shift;
111         my $attrs = shift;
112
113         for my $node ($self->{doc}->findnodes($xpath)) {
114                 my $new = $self->{doc}->createElement($name) if (!$ns);
115                 $new = $self->{doc}->createElementNS($ns,$name) if ($ns);
116
117                 $new->appendChild( $self->{doc}->createTextNode( $text ) )
118                         if (defined $text);
119
120                 if (ref($attrs)) {
121                         for my $key (keys %$attrs) {
122                                 $new->setAttribute( $key => $$attrs{$key} );
123                         }
124                 }
125
126                 $node->appendChild( $new );
127
128                 return $new;
129         }
130 }
131
132 sub add_item {
133         my $self = shift;
134         my $class = ref($self) || $self;
135         $class .= '::item';
136
137         my $item_xml = shift;
138         my $entry = $class->new($item_xml);
139         return undef unless $entry;
140
141         $entry->base($self->base);
142         $entry->unapi($self->unapi);
143
144         $self->push_item($entry);
145         return $entry;
146 }
147
148 sub add_holdings {
149         my $self = shift;
150         my $holdings_xml = shift;
151
152         $parser = new XML::LibXML if (!$parser);
153         my $new_doc = $parser->parse_string($holdings_xml);
154
155         for my $root ( $self->{doc}->findnodes($self->{holdings_xpath}) ) {
156                 $root->appendChild($new_doc->documentElement);
157                 last;
158         }
159         return $self;
160 }
161
162 sub composeDoc {
163         my $self = shift;
164         for my $root ( $self->{doc}->findnodes($self->{item_xpath}) ) {
165                 for my $item ( $self->items ) {
166                         $root->appendChild( $item->{doc}->documentElement );
167                 }
168                 last;
169         }
170 }
171
172 sub toString {
173         my $self = shift;
174         $self->composeDoc;
175         return $self->{doc}->toString(1);
176 }
177
178 sub id {};
179 sub link {};
180 sub title {};
181 sub update_ts {};
182 sub creator {};
183
184 #----------------------------------------------------------
185
186 package OpenILS::WWW::SuperCat::Feed::atom;
187 use base 'OpenILS::WWW::SuperCat::Feed';
188
189 sub new {
190         my $class = shift;
191         my $self = $class->SUPER::build('<atom:feed xmlns:atom="http://www.w3.org/2005/Atom"/>');
192         $self->{type} = 'application/xml';
193         $self->{item_xpath} = '/atom:feed';
194         return $self;
195 }
196
197 sub title {
198         my $self = shift;
199         my $text = shift;
200         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:title', $text);
201 }
202
203 sub update_ts {
204         my $self = shift;
205         my $text = shift;
206         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:updated', $text);
207 }
208
209 sub creator {
210         my $self = shift;
211         my $text = shift;
212         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:author');
213         $self->_create_node('/atom:feed/atom:author', 'http://www.w3.org/2005/Atom','atom:name', $text);
214 }
215
216 sub link {
217         my $self = shift;
218         my $type = shift;
219         my $id = shift;
220         my $mime = shift || "application/x-$type+xml";
221         my $title = shift;
222
223         $type = 'self' if ($type eq 'atom');
224
225         $self->_create_node(
226                 $self->{item_xpath},
227                 'http://www.w3.org/2005/Atom',
228                 'atom:link',
229                 undef,
230                 { rel => $type,
231                   href => $id,
232                   title => $title,
233                   type => $mime,
234                 }
235         );
236 }
237
238 sub id {
239         my $self = shift;
240         my $id = shift;
241
242         $self->_create_node( '/atom:feed', 'http://www.w3.org/2005/Atom', 'atom:id', $id );
243 }
244
245 package OpenILS::WWW::SuperCat::Feed::atom::item;
246 use base 'OpenILS::WWW::SuperCat::Feed::atom';
247
248 sub new {
249         my $class = shift;
250         my $xml = shift;
251         my $self = $class->SUPER::build($xml);
252         $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom');
253         $self->{item_xpath} = '/atom:entry';
254         $self->{holdings_xpath} = '/atom:entry';
255         $self->{type} = 'application/xml';
256         return $self;
257 }
258
259
260 #----------------------------------------------------------
261
262 package OpenILS::WWW::SuperCat::Feed::rss2;
263 use base 'OpenILS::WWW::SuperCat::Feed';
264
265 sub new {
266         my $class = shift;
267         my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
268         $self->{type} = 'application/xml';
269         $self->{item_xpath} = '/rss/channel';
270         return $self;
271 }
272
273 sub title {
274         my $self = shift;
275         my $text = shift;
276         $self->_create_node('/rss/channel',undef,'title', $text);
277 }
278
279 sub update_ts {
280         my $self = shift;
281         my $text = shift;
282         $self->_create_node('/rss/channel',undef,'lastBuildDate', $text);
283 }
284
285 sub creator {
286         my $self = shift;
287         my $text = shift;
288         $self->_create_node('/rss/channel', undef,'generator', $text);
289 }
290
291 sub link {
292         my $self = shift;
293         my $type = shift;
294         my $id = shift;
295         my $mime = shift || "application/x-$type+xml";
296
297         $type = 'self' if ($type eq 'rss2');
298
299         $self->_create_node(
300                 $self->{item_xpath},
301                 undef,
302                 'link',
303                 $id,
304                 { rel => $type,
305                   type => $mime,
306                 }
307         );
308 }
309
310 package OpenILS::WWW::SuperCat::Feed::rss2::item;
311 use base 'OpenILS::WWW::SuperCat::Feed::rss2';
312
313 sub new {
314         my $class = shift;
315         my $xml = shift;
316         my $self = $class->SUPER::build($xml);
317         $self->{type} = 'application/xml';
318         $self->{item_xpath} = '/item';
319         $self->{holdings_xpath} = '/item';
320         return $self;
321 }
322
323
324 #----------------------------------------------------------
325
326 package OpenILS::WWW::SuperCat::Feed::mods;
327 use base 'OpenILS::WWW::SuperCat::Feed';
328
329 sub new {
330         my $class = shift;
331         my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/"/>');
332         $self->{type} = 'application/xml';
333         $self->{item_xpath} = '/mods:modsCollection';
334         return $self;
335 }
336
337 package OpenILS::WWW::SuperCat::Feed::mods::item;
338 use base 'OpenILS::WWW::SuperCat::Feed::mods';
339
340 sub new {
341         my $class = shift;
342         my $xml = shift;
343         my $self = $class->SUPER::build($xml);
344         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', 'mods');
345         $self->{type} = 'application/xml';
346         $self->{holdings_xpath} = '/mods:mods';
347         return $self;
348 }
349
350 my $linkid = 1;
351
352 sub link {
353         my $self = shift;
354         my $type = shift;
355         my $id = shift;
356
357         if ($type eq 'unapi' || $type eq 'opac') {
358                 $self->_create_node(
359                         'mods:mods',
360                         'http://www.loc.gov/mods/',
361                         'mods:relatedItem',
362                         undef,
363                         { type => 'otherFormat', id => 'link-'.$linkid }
364                 );
365                 $self->_create_node(
366                         "mods:mods/mods:relatedItem[\@id='link-$linkid']",
367                         'http://www.loc.gov/mods/',
368                         'mods:recordIdentifier',
369                         $id
370                 );
371                 $linkid++;
372         }
373 }
374
375
376 #----------------------------------------------------------
377
378 package OpenILS::WWW::SuperCat::Feed::mods3;
379 use base 'OpenILS::WWW::SuperCat::Feed::mods';
380
381 sub new {
382         my $class = shift;
383         my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/v3"/>');
384         $self->{type} = 'application/xml';
385         $self->{item_xpath} = '/mods:modsCollection';
386         return $self;
387 }
388
389 package OpenILS::WWW::SuperCat::Feed::mods3::item;
390 use base 'OpenILS::WWW::SuperCat::Feed::mods::item';
391
392 sub new {
393         my $class = shift;
394         my $xml = shift;
395         my $self = $class->SUPER::build($xml);
396         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/v3', 'mods');
397         $self->{type} = 'application/xml';
398         $self->{holdings_xpath} = '/mods:mods';
399         return $self;
400 }
401
402 sub link {
403         my $self = shift;
404         my $type = shift;
405         my $id = shift;
406
407         if ($type eq 'unapi' || $type eq 'opac') {
408                 $self->_create_node(
409                         'mods:mods',
410                         'http://www.loc.gov/mods/v3',
411                         'mods:relatedItem',
412                         undef,
413                         { type => 'otherFormat', id => 'link-'.$linkid }
414                 );
415                 $self->_create_node(
416                         "mods:mods/mods:relatedItem[\@id='link-$linkid']",
417                         'http://www.loc.gov/mods/v3',
418                         'mods:recordIdentifier',
419                         $id
420                 );
421                 $linkid++;
422         }
423 }
424
425
426 #----------------------------------------------------------
427
428 package OpenILS::WWW::SuperCat::Feed::marcxml;
429 use base 'OpenILS::WWW::SuperCat::Feed';
430
431 sub new {
432         my $class = shift;
433         my $self = $class->SUPER::build('<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim"/>');
434         $self->{type} = 'application/xml';
435         $self->{item_xpath} = '/marc:collection';
436         return $self;
437 }
438
439 package OpenILS::WWW::SuperCat::Feed::marcxml::item;
440 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
441
442 sub new {
443         my $class = shift;
444         my $xml = shift;
445         my $self = $class->SUPER::build($xml);
446         return undef unless $self;
447         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/MARC21/slim', 'marc');
448         $self->{type} = 'application/xml';
449         $self->{holdings_xpath} = '/marc:record';
450         return $self;
451 }
452
453 sub link {
454         my $self = shift;
455         my $type = shift;
456         my $id = shift;
457
458         if ($type eq 'unapi' || $type eq 'opac') {
459                 $self->_create_node(
460                         'marc:record',
461                         'http://www.w3.org/1999/xhtml',
462                         'xhtml:link',
463                         undef,
464                         { rel => 'otherFormat', href => $id, title => "Dynamic Details" }
465                 );
466                 $linkid++;
467         }
468 }
469
470
471 #----------------------------------------------------------
472
473 package OpenILS::WWW::SuperCat::Feed::html;
474 use base 'OpenILS::WWW::SuperCat::Feed::atom';
475
476 sub new {
477         my $class = shift;
478         my $self = $class->SUPER::new;
479         $self->type('text/html');
480         return $self;
481 }
482
483 our ($_parser, $_xslt, $xslt_file);
484
485 sub toString {
486         my $self = shift;
487         my $base = $self->base || '';
488         my $root = $self->root || '';
489         my $search = $self->search || '';
490         my $class = $self->class || '';
491         my $lib = $self->lib || '-';
492
493         $self->composeDoc;
494
495         $_parser ||= new XML::LibXML;
496         $_xslt ||= new XML::LibXSLT;
497
498         $xslt_file ||=
499                 OpenSRF::Utils::SettingsClient
500                         ->new
501                         ->config_value( dirs => 'xsl' ).
502                 "/ATOM2XHTML.xsl";
503
504         # parse the MODS xslt ...
505         my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
506
507         my $new_doc = $atom2html_xslt->transform(
508                 $self->{doc},
509                 base_dir => "'$root'",
510                 lib => "'$lib'",
511                 searchTerms => "'$search'",
512                 searchClass => "'$class'",
513         );
514
515         return $new_doc->toString(1); 
516 }
517
518
519 package OpenILS::WWW::SuperCat::Feed::html::item;
520 use base 'OpenILS::WWW::SuperCat::Feed::atom::item';
521
522 #----------------------------------------------------------
523
524 package OpenILS::WWW::SuperCat::Feed::htmlcard;
525 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
526
527 sub new {
528         my $class = shift;
529         my $self = $class->SUPER::new;
530         $self->type('text/html');
531         $self->{xsl} = "/MARC21slim2HTMLCard.xsl";
532         return $self;
533 }
534
535 our ($_parser, $_xslt, $xslt_file);
536
537 sub toString {
538         my $self = shift;
539         my $base = $self->base || '';
540         my $root = $self->root || '';
541         my $search = $self->search || '';
542         my $lib = $self->lib || '-';
543
544         $self->composeDoc;
545
546         $_parser ||= new XML::LibXML;
547         $_xslt ||= new XML::LibXSLT;
548
549         $xslt_file =
550                 OpenSRF::Utils::SettingsClient
551                         ->new
552                         ->config_value( dirs => 'xsl' ).$self->{xsl};
553
554         # parse the MODS xslt ...
555         my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
556
557         my $new_doc = $atom2html_xslt->transform(
558                 $self->{doc},
559                 base_dir => "'$root'",
560                 lib => "'$lib'",
561                 searchTerms => "'$search'",
562         );
563
564         return $new_doc->toString(1); 
565 }
566
567 package OpenILS::WWW::SuperCat::Feed::htmlcard::item;
568 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
569
570 package OpenILS::WWW::SuperCat::Feed::htmlholdings;
571 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard';
572
573 sub new {
574         my $class = shift;
575         my $self = $class->SUPER::new;
576         $self->{xsl} = "/MARC21slim2HTMLCard-holdings.xsl";
577         return $self;
578 }
579
580 package OpenILS::WWW::SuperCat::Feed::htmlholdings::item;
581 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard::item';
582
583 1;