]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm
make double extra sure that the namespace attribute is there!
[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->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom', 0);
295         $self->{item_xpath} = '/atom:entry';
296         $self->{holdings_xpath} = '/atom:entry';
297         $self->{type} = 'application/xml';
298         return $self;
299 }
300
301
302 #----------------------------------------------------------
303
304 package OpenILS::WWW::SuperCat::Feed::rss2;
305 use base 'OpenILS::WWW::SuperCat::Feed';
306
307 sub new {
308         my $class = shift;
309         my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
310         $self->{type} = 'application/xml';
311         $self->{item_xpath} = '/rss/channel';
312         return $self;
313 }
314
315 sub title {
316         my $self = shift;
317         my $text = shift;
318         $self->_create_node('/rss/channel',undef,'title', $text);
319 }
320
321 sub update_ts {
322         my $self = shift;
323         my $text = shift;
324         $self->_create_node($self->{item_xpath},undef,'lastBuildDate', $text);
325 }
326
327 sub creator {
328         my $self = shift;
329         my $text = shift;
330         $self->_create_node('/rss/channel', undef,'generator', $text);
331 }
332
333 sub link {
334         my $self = shift;
335         my $type = shift;
336         my $id = shift;
337         my $mime = shift || "application/x-$type+xml";
338
339         $type = 'self' if ($type eq 'rss2');
340
341         $self->_create_node(
342                 $self->{item_xpath},
343                 undef,
344                 'link',
345                 $id,
346                 { rel => $type,
347                   type => $mime,
348                 }
349         );
350 }
351
352 package OpenILS::WWW::SuperCat::Feed::rss2::item;
353 use base 'OpenILS::WWW::SuperCat::Feed::rss2';
354
355 sub new {
356         my $class = shift;
357         my $xml = shift;
358         my $self = $class->SUPER::build($xml);
359         $self->{type} = 'application/xml';
360         $self->{item_xpath} = '/item';
361         $self->{holdings_xpath} = '/item';
362         return $self;
363 }
364
365 sub update_ts {
366         my $self = shift;
367         my $text = shift;
368         $self->_create_node($self->{item_xpath},undef,'pubDate', $text);
369 }
370
371
372
373 #----------------------------------------------------------
374
375 package OpenILS::WWW::SuperCat::Feed::mods;
376 use base 'OpenILS::WWW::SuperCat::Feed';
377
378 sub new {
379         my $class = shift;
380         my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/"/>');
381         $self->{type} = 'application/xml';
382         $self->{item_xpath} = '/mods:modsCollection';
383         return $self;
384 }
385
386 package OpenILS::WWW::SuperCat::Feed::mods::item;
387 use base 'OpenILS::WWW::SuperCat::Feed::mods';
388
389 sub new {
390         my $class = shift;
391         my $xml = shift;
392         my $self = $class->SUPER::build($xml);
393         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', 'mods');
394         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', 'mods', 0);
395         $self->{type} = 'application/xml';
396         $self->{holdings_xpath} = '/mods:mods';
397         return $self;
398 }
399
400 my $linkid = 1;
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/',
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/',
418                         'mods:recordIdentifier',
419                         $id
420                 );
421                 $linkid++;
422         }
423 }
424
425
426 #----------------------------------------------------------
427
428 package OpenILS::WWW::SuperCat::Feed::mods3;
429 use base 'OpenILS::WWW::SuperCat::Feed::mods';
430
431 sub new {
432         my $class = shift;
433         my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/v3"/>');
434         $self->{type} = 'application/xml';
435         $self->{item_xpath} = '/mods:modsCollection';
436         return $self;
437 }
438
439 package OpenILS::WWW::SuperCat::Feed::mods3::item;
440 use base 'OpenILS::WWW::SuperCat::Feed::mods::item';
441
442 sub new {
443         my $class = shift;
444         my $xml = shift;
445         my $self = $class->SUPER::build($xml);
446         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/v3', 'mods');
447         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/v3', 'mods', 0);
448         $self->{type} = 'application/xml';
449         $self->{holdings_xpath} = '/mods:mods';
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                         'mods:mods',
461                         'http://www.loc.gov/mods/v3',
462                         'mods:relatedItem',
463                         undef,
464                         { type => 'otherFormat', id => 'link-'.$linkid }
465                 );
466                 $self->_create_node(
467                         "mods:mods/mods:relatedItem[\@id='link-$linkid']",
468                         'http://www.loc.gov/mods/v3',
469                         'mods:recordIdentifier',
470                         $id
471                 );
472                 $linkid++;
473         }
474 }
475
476
477 #----------------------------------------------------------
478
479 package OpenILS::WWW::SuperCat::Feed::marcxml;
480 use base 'OpenILS::WWW::SuperCat::Feed';
481
482 sub new {
483         my $class = shift;
484         my $self = $class->SUPER::build('<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim"/>');
485         $self->{type} = 'application/xml';
486         $self->{item_xpath} = '/marc:collection';
487         return $self;
488 }
489 sub link {
490         my $self = shift;
491         my $type = shift;
492         my $id = shift;
493
494         if ($type eq 'unapi') {
495                 $self->_create_node(
496                         'marc:collection',
497                         'http://www.w3.org/1999/xhtml',
498                         'xhtml:link',
499                         undef,
500                         { rel => 'unapi-server', href => $id, title => "unapi" }
501                 );
502                 $linkid++;
503         }
504 }
505
506
507 package OpenILS::WWW::SuperCat::Feed::marcxml::item;
508 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
509
510 sub new {
511         my $class = shift;
512         my $xml = shift;
513         my $self = $class->SUPER::build($xml);
514         return undef unless $self;
515         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/MARC21/slim', 'marc');
516         $self->{doc}->documentElement->setNamespace('http://www.loc.gov/MARC21/slim', 'marc', 0);
517         $self->{type} = 'application/xml';
518         $self->{holdings_xpath} = '/marc: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                         'marc: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                         'marc: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;