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