]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm
making the dumpac mo betta
[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->{type} = 'application/xml';
234         $self->{item_xpath} = '/atom:feed';
235         return $self;
236 }
237
238 sub title {
239         my $self = shift;
240         my $text = shift;
241         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','title', $text);
242 }
243
244 sub update_ts {
245         my $self = shift;
246         my $text = shift;
247         $self->_create_node($self->{item_xpath},'http://www.w3.org/2005/Atom','updated', $text);
248 }
249
250 sub creator {
251         my $self = shift;
252         my $text = shift;
253         $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','author');
254         $self->_create_node('/atom:feed/atom:author', 'http://www.w3.org/2005/Atom','name', $text);
255 }
256
257 sub link {
258         my $self = shift;
259         my $type = shift;
260         my $id = shift;
261         my $mime = shift || "application/x-$type+xml";
262         my $title = shift;
263
264         $type = 'self' if ($type eq 'atom');
265
266         $self->_create_node(
267                 $self->{item_xpath},
268                 'http://www.w3.org/2005/Atom',
269                 'link',
270                 undef,
271                 { rel => $type,
272                   href => $id,
273                   title => $title,
274                   type => $mime,
275                 }
276         );
277 }
278
279 sub id {
280         my $self = shift;
281         my $id = shift;
282
283         $self->_create_node( '/atom:feed', 'http://www.w3.org/2005/Atom', 'id', $id );
284 }
285
286 package OpenILS::WWW::SuperCat::Feed::atom::item;
287 use base 'OpenILS::WWW::SuperCat::Feed::atom';
288
289 sub new {
290         my $class = shift;
291         my $xml = shift;
292         my $self = $class->SUPER::build($xml);
293         $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom');
294         $self->{item_xpath} = '/atom:entry';
295         $self->{holdings_xpath} = '/atom:entry';
296         $self->{type} = 'application/xml';
297         return $self;
298 }
299
300
301 #----------------------------------------------------------
302
303 package OpenILS::WWW::SuperCat::Feed::rss2;
304 use base 'OpenILS::WWW::SuperCat::Feed';
305
306 sub new {
307         my $class = shift;
308         my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
309         $self->{type} = 'application/xml';
310         $self->{item_xpath} = '/rss/channel';
311         return $self;
312 }
313
314 sub title {
315         my $self = shift;
316         my $text = shift;
317         $self->_create_node('/rss/channel',undef,'title', $text);
318 }
319
320 sub update_ts {
321         my $self = shift;
322         my $text = shift;
323         $self->_create_node($self->{item_xpath},undef,'lastBuildDate', $text);
324 }
325
326 sub creator {
327         my $self = shift;
328         my $text = shift;
329         $self->_create_node('/rss/channel', undef,'generator', $text);
330 }
331
332 sub link {
333         my $self = shift;
334         my $type = shift;
335         my $id = shift;
336         my $mime = shift || "application/x-$type+xml";
337
338         $type = 'self' if ($type eq 'rss2');
339
340         $self->_create_node(
341                 $self->{item_xpath},
342                 undef,
343                 'link',
344                 $id,
345                 { rel => $type,
346                   type => $mime,
347                 }
348         );
349 }
350
351 package OpenILS::WWW::SuperCat::Feed::rss2::item;
352 use base 'OpenILS::WWW::SuperCat::Feed::rss2';
353
354 sub new {
355         my $class = shift;
356         my $xml = shift;
357         my $self = $class->SUPER::build($xml);
358         $self->{type} = 'application/xml';
359         $self->{item_xpath} = '/item';
360         $self->{holdings_xpath} = '/item';
361         return $self;
362 }
363
364 sub update_ts {
365         my $self = shift;
366         my $text = shift;
367         $self->_create_node($self->{item_xpath},undef,'pubDate', $text);
368 }
369
370
371
372 #----------------------------------------------------------
373
374 package OpenILS::WWW::SuperCat::Feed::mods;
375 use base 'OpenILS::WWW::SuperCat::Feed';
376
377 sub new {
378         my $class = shift;
379         my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/"/>');
380         $self->{type} = 'application/xml';
381         $self->{item_xpath} = '/mods:modsCollection';
382         return $self;
383 }
384
385 package OpenILS::WWW::SuperCat::Feed::mods::item;
386 use base 'OpenILS::WWW::SuperCat::Feed::mods';
387
388 sub new {
389         my $class = shift;
390         my $xml = shift;
391         my $self = $class->SUPER::build($xml);
392         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', 'mods');
393         $self->{type} = 'application/xml';
394         $self->{holdings_xpath} = '/mods:mods';
395         return $self;
396 }
397
398 my $linkid = 1;
399
400 sub link {
401         my $self = shift;
402         my $type = shift;
403         my $id = shift;
404
405         if ($type eq 'unapi' || $type eq 'opac') {
406                 $self->_create_node(
407                         'mods:mods',
408                         'http://www.loc.gov/mods/',
409                         'mods:relatedItem',
410                         undef,
411                         { type => 'otherFormat', id => 'link-'.$linkid }
412                 );
413                 $self->_create_node(
414                         "mods:mods/mods:relatedItem[\@id='link-$linkid']",
415                         'http://www.loc.gov/mods/',
416                         'mods:recordIdentifier',
417                         $id
418                 );
419                 $linkid++;
420         }
421 }
422
423
424 #----------------------------------------------------------
425
426 package OpenILS::WWW::SuperCat::Feed::mods3;
427 use base 'OpenILS::WWW::SuperCat::Feed::mods';
428
429 sub new {
430         my $class = shift;
431         my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/v3"/>');
432         $self->{type} = 'application/xml';
433         $self->{item_xpath} = '/mods:modsCollection';
434         return $self;
435 }
436
437 package OpenILS::WWW::SuperCat::Feed::mods3::item;
438 use base 'OpenILS::WWW::SuperCat::Feed::mods::item';
439
440 sub new {
441         my $class = shift;
442         my $xml = shift;
443         my $self = $class->SUPER::build($xml);
444         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/v3', 'mods');
445         $self->{type} = 'application/xml';
446         $self->{holdings_xpath} = '/mods:mods';
447         return $self;
448 }
449
450 sub link {
451         my $self = shift;
452         my $type = shift;
453         my $id = shift;
454
455         if ($type eq 'unapi' || $type eq 'opac') {
456                 $self->_create_node(
457                         'mods:mods',
458                         'http://www.loc.gov/mods/v3',
459                         'mods:relatedItem',
460                         undef,
461                         { type => 'otherFormat', id => 'link-'.$linkid }
462                 );
463                 $self->_create_node(
464                         "mods:mods/mods:relatedItem[\@id='link-$linkid']",
465                         'http://www.loc.gov/mods/v3',
466                         'mods:recordIdentifier',
467                         $id
468                 );
469                 $linkid++;
470         }
471 }
472
473
474 #----------------------------------------------------------
475
476 package OpenILS::WWW::SuperCat::Feed::marcxml;
477 use base 'OpenILS::WWW::SuperCat::Feed';
478
479 sub new {
480         my $class = shift;
481         my $self = $class->SUPER::build('<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim"/>');
482         $self->{type} = 'application/xml';
483         $self->{item_xpath} = '/marc:collection';
484         return $self;
485 }
486 sub link {
487         my $self = shift;
488         my $type = shift;
489         my $id = shift;
490
491         if ($type eq 'unapi') {
492                 $self->_create_node(
493                         'marc:collection',
494                         'http://www.w3.org/1999/xhtml',
495                         'xhtml:link',
496                         undef,
497                         { rel => 'unapi-server', href => $id, title => "unapi" }
498                 );
499                 $linkid++;
500         }
501 }
502
503
504 package OpenILS::WWW::SuperCat::Feed::marcxml::item;
505 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
506
507 sub new {
508         my $class = shift;
509         my $xml = shift;
510         my $self = $class->SUPER::build($xml);
511         return undef unless $self;
512         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/MARC21/slim', 'marc');
513         $self->{type} = 'application/xml';
514         $self->{holdings_xpath} = '/marc:record';
515         return $self;
516 }
517
518 sub link {
519         my $self = shift;
520         my $type = shift;
521         my $id = shift;
522
523         if ($type eq 'opac') {
524                 $self->_create_node(
525                         'marc:record',
526                         'http://www.w3.org/1999/xhtml',
527                         'xhtml:link',
528                         undef,
529                         { rel => 'otherFormat', href => $id, title => "Dynamic Details" }
530                 );
531                 $linkid++;
532         } elsif ($type eq 'unapi-id') {
533                 $self->_create_node(
534                         'marc:record',
535                         'http://www.w3.org/1999/xhtml',
536                         'xhtml:abbr',
537                         undef,
538                         {  title => $id, class => "unapi-id" }
539                 );
540                 $linkid++;
541         }
542 }
543
544
545 #----------------------------------------------------------
546
547 package OpenILS::WWW::SuperCat::Feed::html;
548 use base 'OpenILS::WWW::SuperCat::Feed::atom';
549
550 sub new {
551         my $class = shift;
552         my $self = $class->SUPER::new;
553         $self->type('text/html');
554         return $self;
555 }
556
557 our ($_parser, $_xslt, $xslt_file);
558
559 sub toString {
560         my $self = shift;
561         my $base = $self->base || '';
562         my $root = $self->root || '';
563         my $search = $self->search || '';
564         my $class = $self->class || '';
565         my $lib = $self->lib || '-';
566
567         $self->composeDoc;
568
569         $_parser ||= new XML::LibXML;
570         $_xslt ||= new XML::LibXSLT;
571
572         $xslt_file ||=
573                 OpenSRF::Utils::SettingsClient
574                         ->new
575                         ->config_value( dirs => 'xsl' ).
576                 "/ATOM2XHTML.xsl";
577
578         # parse the MODS xslt ...
579         my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
580
581         my $new_doc = $atom2html_xslt->transform(
582                 $self->{doc},
583                 base_dir => "'$root'",
584                 lib => "'$lib'",
585                 searchTerms => "'$search'",
586                 searchClass => "'$class'",
587         );
588
589         return $new_doc->toString(1); 
590 }
591
592
593 package OpenILS::WWW::SuperCat::Feed::html::item;
594 use base 'OpenILS::WWW::SuperCat::Feed::atom::item';
595
596 #----------------------------------------------------------
597
598 package OpenILS::WWW::SuperCat::Feed::htmlcard;
599 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
600
601 sub new {
602         my $class = shift;
603         my $self = $class->SUPER::new;
604         $self->type('text/html');
605         $self->{xsl} = "/MARC21slim2HTMLCard.xsl";
606         return $self;
607 }
608
609 our ($_parser, $_xslt, $xslt_file);
610
611 sub toString {
612         my $self = shift;
613         my $base = $self->base || '';
614         my $root = $self->root || '';
615         my $search = $self->search || '';
616         my $sort = $self->Sort || '';
617         my $sort_dir = $self->SortDir || '';
618         my $lang = $self->lang || '';
619         my $lib = $self->lib || '-';
620
621         $self->composeDoc;
622
623         $_parser ||= new XML::LibXML;
624         $_xslt ||= new XML::LibXSLT;
625
626         $xslt_file =
627                 OpenSRF::Utils::SettingsClient
628                         ->new
629                         ->config_value( dirs => 'xsl' ).$self->{xsl};
630
631         # parse the MODS xslt ...
632         my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
633
634         my $new_doc = $atom2html_xslt->transform(
635                 $self->{doc},
636                 base_dir => "'$root'",
637                 lib => "'$lib'",
638                 searchTerms => "'$search'",
639                 searchSort => "'$sort'",
640                 searchSortDir => "'$sort_dir'",
641                 searchLang => "'$lang'",
642         );
643
644         return $new_doc->toString(1); 
645 }
646
647 package OpenILS::WWW::SuperCat::Feed::htmlcard::item;
648 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
649
650 package OpenILS::WWW::SuperCat::Feed::htmlholdings;
651 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard';
652
653 sub new {
654         my $class = shift;
655         my $self = $class->SUPER::new;
656         $self->{xsl} = "/MARC21slim2HTMLCard-holdings.xsl";
657         return $self;
658 }
659
660 package OpenILS::WWW::SuperCat::Feed::htmlholdings::item;
661 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard::item';
662
663 1;