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