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