LP#1552778: copy some date/time utils from OpenSRF
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / 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 use DateTime;
10 use DateTime::Format::Mail;
11
12
13 sub exists {
14     my $class = shift;
15     my $type = shift;
16
17     return 1 if UNIVERSAL::can("OpenILS::WWW::SuperCat::Feed::$type" => 'new');
18     return 0;
19 }
20
21 sub new {
22     my $class = shift;
23     my $type = shift;
24     if ($type) {
25         $class .= '::'.$type;
26         return $class->new;
27     }
28     throw OpenSRF::EX::ERROR ("I need a feed type!") ;
29 }
30
31 sub build {
32     my $class = shift;
33     my $xml = shift;
34     return undef unless $xml;
35
36     $parser = new XML::LibXML if (!$parser);
37
38     my $self = { doc => $parser->parse_string($xml), items => [] };
39
40     $self = bless $self => $class;
41     $self->{count} = 0;
42     return $self;
43 }
44
45 sub type {
46     my $self = shift;
47     my $type = shift;
48     $self->{type} = $type if ($type);
49     return $self->{type};
50 }
51
52 sub count {
53     my $self = shift;
54     return $self->{count};
55 }
56
57 sub search {
58     my $self = shift;
59     my $search = shift;
60     $self->{search} = $search if ($search);
61     return $self->{search};
62 }
63
64 sub class {
65     my $self = shift;
66     my $search = shift;
67     $self->{class} = $search if ($search);
68     return $self->{class};
69 }
70
71 sub Sort {
72     my $self = shift;
73     my $search = shift;
74     $self->{sort} = $search if ($search);
75     return $self->{sort};
76 }
77
78 sub SortDir {
79     my $self = shift;
80     my $search = shift;
81     $self->{sort_dir} = $search if ($search);
82     return $self->{sort_dir};
83 }
84
85 sub lang {
86     my $self = shift;
87     my $search = shift;
88     $self->{lang} = $search if ($search);
89     return $self->{lang};
90 }
91
92 sub lib {
93     my $self = shift;
94     my $lib = shift;
95     $self->{lib} = $lib if ($lib);
96     return $self->{lib};
97 }
98
99 sub base {
100     my $self = shift;
101     my $base = shift;
102     $self->{base} = $base if ($base);
103     return $self->{base};
104 }
105
106 sub root {
107     my $self = shift;
108     my $root = shift;
109     $self->{root} = $root if ($root);
110     return $self->{root};
111 }
112
113 sub unapi {
114     my $self = shift;
115     my $unapi = shift;
116     $self->{unapi} = $unapi if ($unapi);
117     return $self->{unapi};
118 }
119
120 sub push_item {
121     my $self = shift;
122     $self->{count} += scalar(@_);
123     push @{ $self->{items} }, @_;
124 }
125
126 sub items {
127     my $self = shift;
128     return @{ $self->{items} } if (wantarray);
129     return $self->{items};
130 }
131
132 sub _add_node {
133     my $self = shift;
134
135     my $xpath = shift;
136     my $new = shift;
137
138     for my $node ($self->{doc}->findnodes($xpath)) {
139         $node->appendChild($new);
140         last;
141     }
142 }
143
144 sub _create_node {
145     my $self = shift;
146
147     my $xpath = shift;
148     my $ns = shift;
149     my $name = shift;
150     my $text = shift;
151     my $attrs = shift;
152
153     for my $node ($self->{doc}->findnodes($xpath)) {
154         my $new = $self->{doc}->createElement($name) if (!$ns);
155         $new = $self->{doc}->createElementNS($ns,$name) if ($ns);
156
157         $new->appendChild( $self->{doc}->createTextNode( $text ) )
158             if (defined $text);
159
160         if (ref($attrs)) {
161             for my $key (keys %$attrs) {
162                 next unless $$attrs{$key};
163                 $new->setAttribute( $key => $$attrs{$key} );
164             }
165         }
166
167         $node->appendChild( $new );
168
169         return $new;
170     }
171 }
172
173 sub add_item {
174     my $self = shift;
175     my $class = ref($self) || $self;
176     $class .= '::item';
177
178     my $item_xml = shift;
179     my $entry = $class->new($item_xml);
180     return undef unless $entry;
181
182     $entry->base($self->base);
183     $entry->unapi($self->unapi);
184
185     $self->push_item($entry);
186     return $entry;
187 }
188
189 sub add_holdings {
190     my $self = shift;
191     my $holdings_xml = shift;
192
193     return $self unless ($holdings_xml);
194
195     $parser = new XML::LibXML if (!$parser);
196     my $new_doc = $parser->parse_string($holdings_xml);
197
198     for my $root ( $self->{doc}->findnodes($self->{holdings_xpath}) ) {
199         $root->appendChild($new_doc->documentElement);
200         last;
201     }
202     return $self;
203 }
204
205 sub composeDoc {
206     my $self = shift;
207     for my $root ( $self->{doc}->findnodes($self->{item_xpath}) ) {
208         for my $item ( $self->items ) {
209             $root->appendChild( $item->{doc}->documentElement );
210         }
211         last;
212     }
213 }
214
215 sub toString {
216     my $self = shift;
217     $self->composeDoc;
218     return $self->{doc}->toString(1);
219 }
220
221 sub id {};
222 sub link {};
223 sub title {};
224 sub update_ts {};
225 sub creator {};
226 sub description {};
227
228 #----------------------------------------------------------
229
230 package OpenILS::WWW::SuperCat::Feed::atom;
231 use base 'OpenILS::WWW::SuperCat::Feed';
232 use OpenILS::Utils::DateTime qw/:datetime/;
233
234 sub new {
235     my $class = shift;
236     my $self = $class->SUPER::build('<feed xmlns:atom="http://www.w3.org/2005/Atom"/>');
237     $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', undef);
238     $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom');
239     $self->{type} = 'application/atom+xml';
240     $self->{item_xpath} = '/atom:feed';
241     return $self;
242 }
243
244 sub title {
245     my $self = shift;
246     my $text = shift;
247     $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','title', $text);
248 }
249
250 sub update_ts {
251     my $self = shift;
252     # ATOM demands RFC-3339 compliant datetime formats
253     my $text = shift || gmtime_ISO8601();
254     $self->_create_node($self->{item_xpath},'http://www.w3.org/2005/Atom','updated', $text);
255 }
256
257 sub creator {
258     my $self = shift;
259     my $text = shift;
260     $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','author');
261     $self->_create_node('/atom:feed/atom:author', 'http://www.w3.org/2005/Atom','name', $text);
262 }
263
264 sub link {
265     my $self = shift;
266     my $type = shift;
267     my $id = shift;
268     my $mime = shift || "application/x-$type+xml";
269     my $title = shift;
270
271     $type = 'self' if ($type eq 'atom');
272
273     $self->_create_node(
274         $self->{item_xpath},
275         'http://www.w3.org/2005/Atom',
276         'link',
277         undef,
278         { rel => $type,
279           href => $id,
280           title => $title,
281           type => $mime,
282         }
283     );
284 }
285
286 sub id {
287     my $self = shift;
288     my $id = shift;
289
290     $self->_create_node( $self->{item_xpath}, 'http://www.w3.org/2005/Atom', 'id', $id );
291 }
292
293 package OpenILS::WWW::SuperCat::Feed::atom::item;
294 use base 'OpenILS::WWW::SuperCat::Feed::atom';
295
296 sub new {
297     my $class = shift;
298     my $xml = shift;
299     my $self = $class->SUPER::build($xml);
300     $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', undef);
301     $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom');
302     $self->{item_xpath} = '/atom:entry';
303     $self->{holdings_xpath} = '/atom:entry';
304     $self->{type} = 'application/atom+xml';
305     return $self;
306 }
307
308
309 #----------------------------------------------------------
310
311 package OpenILS::WWW::SuperCat::Feed::rss2;
312 use base 'OpenILS::WWW::SuperCat::Feed';
313
314 sub new {
315     my $class = shift;
316     my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
317     $self->{type} = 'application/rss+xml';
318     $self->{item_xpath} = '/rss/channel';
319     return $self;
320 }
321
322 sub title {
323     my $self = shift;
324     my $text = shift;
325     $self->_create_node('/rss/channel',undef,'title', $text);
326 }
327
328 sub description {
329     my $self = shift;
330     my $text = shift;
331     $self->_create_node('/rss/channel',undef,'description', $text);
332 }
333
334 sub update_ts {
335     my $self = shift;
336     # RSS2 demands RFC-822 compliant datetime formats
337     my $text = shift || DateTime::Format::Mail->format_datetime(DateTime->now());
338     $self->_create_node($self->{item_xpath},undef,'lastBuildDate', $text);
339 }
340
341 sub creator {
342     my $self = shift;
343     my $text = shift;
344     $self->_create_node('/rss/channel', undef,'generator', $text);
345 }
346
347 sub link {
348     my $self = shift;
349     my $type = shift;
350     my $id = shift;
351     my $mime = shift || "application/x-$type+xml";
352
353     if ($type eq 'rss2' or $type eq 'alternate') {
354         # Just link to ourself using standard RSS2 link element
355         $self->_create_node(
356             $self->{item_xpath},
357             undef,
358             'link',
359             $id,
360             undef
361         );
362     } else {
363         # Alternate link: use XHTML link element
364         $self->_create_node(
365             $self->{item_xpath},
366             'http://www.w3.org/1999/xhtml',
367             'xhtml:link',
368             $id,
369             { rel => $type,
370               type => $mime,
371             }
372         );
373     }
374 }
375
376 sub id {
377     my $self = shift;
378     my $id = shift;
379
380     $self->_create_node($self->{item_xpath}, undef,'guid', $id);
381 }
382
383 package OpenILS::WWW::SuperCat::Feed::rss2::item;
384 use base 'OpenILS::WWW::SuperCat::Feed::rss2';
385
386 sub new {
387     my $class = shift;
388     my $xml = shift;
389     my $self = $class->SUPER::build($xml);
390     $self->{type} = 'application/rss+xml';
391     $self->{item_xpath} = '/item';
392     $self->{holdings_xpath} = '/item';
393     return $self;
394 }
395
396 sub update_ts {
397     my $self = shift;
398     # RSS2 demands RFC-822 compliant datetime formats
399     my $text = shift;
400     if (!$text) {
401         # No date passed in, default to now
402         $text = DateTime::Format::Mail->format_datetime(DateTime->now());
403     } elsif ($text =~ m/^\s*(\d{4})\.?\s*$/o) {
404         # Publication date is just a year, convert accordingly
405         my $year = DateTime->new(year=>$1);
406         $text = DateTime::Format::Mail->format_datetime($year);
407     }
408     $self->_create_node($self->{item_xpath},undef,'pubDate', $text);
409 }
410
411 sub id {
412     my $self = shift;
413     my $id = shift;
414
415     $self->_create_node(
416         $self->{item_xpath},
417         undef,
418         'guid',
419         $id,
420         {
421             isPermaLink=>"false"
422         }
423     );
424 }
425
426 #----------------------------------------------------------
427
428 package OpenILS::WWW::SuperCat::Feed::mods;
429 use base 'OpenILS::WWW::SuperCat::Feed';
430
431 sub new {
432     my $class = shift;
433     my $self = $class->SUPER::build('<modsCollection version="3.0" xmlns="http://www.loc.gov/mods/" xmlns:mods="http://www.loc.gov/mods/"/>');
434     $self->{type} = 'application/xml';
435     $self->{item_xpath} = '/mods:modsCollection';
436     return $self;
437 }
438
439 package OpenILS::WWW::SuperCat::Feed::mods::item;
440 use base 'OpenILS::WWW::SuperCat::Feed::mods';
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/', 'mods');
447     $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', undef, 1);
448     $self->{type} = 'application/xml';
449     $self->{holdings_xpath} = '/mods:mods';
450     return $self;
451 }
452
453 my $linkid = 1;
454
455 sub link {
456     my $self = shift;
457     my $type = shift;
458     my $id = shift;
459
460     if ($type eq 'unapi' || $type eq 'opac') {
461         $self->_create_node(
462             'mods:mods',
463             undef,
464             'relatedItem',
465             undef,
466             { type => 'otherFormat', id => 'link-'.$linkid }
467         );
468         $self->_create_node(
469             "mods:mods/relatedItem[\@id='link-$linkid']",
470             undef,
471             'recordIdentifier',
472             $id
473         );
474         $linkid++;
475     }
476 }
477
478 #----------------------------------------------------------
479
480 package OpenILS::WWW::SuperCat::Feed::mods3;
481 use base 'OpenILS::WWW::SuperCat::Feed::mods';
482
483 sub new {
484     my $class = shift;
485     my $self = $class->SUPER::build('<modsCollection version="3.0" xmlns="http://www.loc.gov/mods/v3" xmlns:mods="http://www.loc.gov/mods/v3"/>');
486     $self->{type} = 'application/xml';
487     $self->{item_xpath} = '/mods:modsCollection';
488     return $self;
489 }
490
491 package OpenILS::WWW::SuperCat::Feed::mods3::item;
492 use base 'OpenILS::WWW::SuperCat::Feed::mods::item';
493
494 sub new {
495     my $class = shift;
496     my $xml = shift;
497     my $self = $class->SUPER::build($xml);
498     $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/v3', 'mods');
499     $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/v3', undef, 1);
500     $self->{type} = 'application/xml';
501     $self->{holdings_xpath} = '/mods:mods';
502     return $self;
503 }
504
505 sub link {
506     my $self = shift;
507     my $type = shift;
508     my $id = shift;
509
510     if ($type eq 'unapi' || $type eq 'opac') {
511         $self->_create_node(
512             'mods:mods',
513             undef,
514             'relatedItem',
515             undef,
516             { type => 'otherFormat', id => 'link-'.$linkid }
517         );
518         $self->_create_node(
519             "mods:mods/relatedItem[\@id='link-$linkid']",
520             undef,
521             'recordIdentifier',
522             $id
523         );
524         $linkid++;
525     }
526 }
527
528
529 #----------------------------------------------------------
530
531 package OpenILS::WWW::SuperCat::Feed::mods32;
532 use base 'OpenILS::WWW::SuperCat::Feed::mods3';
533
534 sub new {
535     my $class = shift;
536     my $self = $class->SUPER::build('<modsCollection version="3.2" xmlns="http://www.loc.gov/mods/v3" xmlns:mods="http://www.loc.gov/mods/v3"/>');
537     $self->{type} = 'application/xml';
538     $self->{item_xpath} = '/mods:modsCollection';
539     return $self;
540 }
541
542 package OpenILS::WWW::SuperCat::Feed::mods32::item;
543 use base 'OpenILS::WWW::SuperCat::Feed::mods3::item';
544
545 #----------------------------------------------------------
546
547 package OpenILS::WWW::SuperCat::Feed::mods33;
548 use base 'OpenILS::WWW::SuperCat::Feed::mods3';
549
550 sub new {
551     my $class = shift;
552     my $self = $class->SUPER::build('<modsCollection version="3.3" xmlns="http://www.loc.gov/mods/v3" xmlns:mods="http://www.loc.gov/mods/v3"/>');
553     $self->{type} = 'application/xml';
554     $self->{item_xpath} = '/mods:modsCollection';
555     return $self;
556 }
557
558 package OpenILS::WWW::SuperCat::Feed::mods33::item;
559 use base 'OpenILS::WWW::SuperCat::Feed::mods3::item';
560
561
562 #----------------------------------------------------------
563
564 package OpenILS::WWW::SuperCat::Feed::marcxml;
565 use base 'OpenILS::WWW::SuperCat::Feed';
566
567 sub new {
568     my $class = shift;
569     my $self = $class->SUPER::build('<collection xmlns="http://www.loc.gov/MARC21/slim" xmlns:marc="http://www.loc.gov/MARC21/slim"/>');
570     $self->{type} = 'application/xml';
571     $self->{item_xpath} = '/marc:collection';
572     return $self;
573 }
574 sub link {
575     my $self = shift;
576     my $type = shift;
577     my $id = shift;
578
579     if ($type eq 'unapi') {
580         $self->_create_node(
581             'marc:collection',
582             'http://www.w3.org/1999/xhtml',
583             'xhtml:link',
584             undef,
585             { rel => 'unapi-server', href => $id, title => "unapi" }
586         );
587         $linkid++;
588     }
589 }
590
591
592 package OpenILS::WWW::SuperCat::Feed::marcxml::item;
593 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
594
595 sub new {
596     my $class = shift;
597     my $xml = shift;
598     my $self = $class->SUPER::build($xml);
599     return undef unless $self;
600     $self->{doc}->documentElement->setNamespace('http://www.loc.gov/MARC21/slim', undef);
601     $self->{type} = 'application/xml';
602     $self->{holdings_xpath} = '/*[local-name()="record"]';
603     return $self;
604 }
605
606 sub link {
607     my $self = shift;
608     my $type = shift;
609     my $id = shift;
610
611     if ($type eq 'opac') {
612         $self->_create_node(
613             '*[local-name()="record"]',
614             'http://www.w3.org/1999/xhtml',
615             'xhtml:link',
616             undef,
617             { rel => 'otherFormat', href => $id, title => "Dynamic Details" }
618         );
619         $linkid++;
620     } elsif ($type eq 'unapi-id') {
621         $self->_create_node(
622             '*[local-name()="record"]',
623             'http://www.w3.org/1999/xhtml',
624             'xhtml:abbr',
625             undef,
626             {  title => $id, class => "unapi-id" }
627         );
628         $linkid++;
629     }
630 }
631
632
633 #----------------------------------------------------------
634
635 package OpenILS::WWW::SuperCat::Feed::html;
636 use base 'OpenILS::WWW::SuperCat::Feed::atom';
637
638 sub new {
639     my $class = shift;
640     my $self = $class->SUPER::new;
641     $self->type('text/html');
642     return $self;
643 }
644
645 our ($_parser, $_xslt, $xslt_file);
646
647 sub toString {
648     my $self = shift;
649     my $base = $self->base || '';
650     my $root = $self->root || '';
651     my $search = $self->search || '';
652     my $class = $self->class || '';
653     my $lib = $self->lib || '-';
654
655     $self->composeDoc;
656
657         $_parser ||= new XML::LibXML;
658         $_xslt ||= new XML::LibXSLT;
659
660     $xslt_file ||=
661                 OpenSRF::Utils::SettingsClient
662                         ->new
663                         ->config_value( dirs => 'xsl' ).
664                 "/ATOM2XHTML.xsl";
665
666         # parse the MODS xslt ...
667         my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
668
669     my $new_doc = $atom2html_xslt->transform(
670         $self->{doc},
671         base_dir => "'$root'",
672         lib => "'$lib'",
673         searchTerms => "'$search'",
674         searchClass => "'$class'",
675     );
676
677     return $new_doc->toString(1); 
678 }
679
680
681 package OpenILS::WWW::SuperCat::Feed::html::item;
682 use base 'OpenILS::WWW::SuperCat::Feed::atom::item';
683
684 #----------------------------------------------------------
685
686 package OpenILS::WWW::SuperCat::Feed::htmlcard;
687 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
688
689 sub new {
690     my $class = shift;
691     my $self = $class->SUPER::new;
692     $self->type('text/html');
693     $self->{xsl} = "/MARC21slim2HTMLCard.xsl";
694     return $self;
695 }
696
697 our ($_parser, $_xslt, $xslt_file);
698
699 sub toString {
700     my $self = shift;
701     my $base = $self->base || '';
702     my $root = $self->root || '';
703     my $search = $self->search || '';
704     my $sort = $self->Sort || '';
705     my $sort_dir = $self->SortDir || '';
706     my $lang = $self->lang || '';
707     my $lib = $self->lib || '-';
708
709     $self->composeDoc;
710
711         $_parser ||= new XML::LibXML;
712         $_xslt ||= new XML::LibXSLT;
713
714     $xslt_file =
715                 OpenSRF::Utils::SettingsClient
716                         ->new
717                         ->config_value( dirs => 'xsl' ).$self->{xsl};
718
719         # parse the MODS xslt ...
720         my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
721
722     my $new_doc = $atom2html_xslt->transform(
723         $self->{doc},
724         base_dir => "'$root'",
725         lib => "'$lib'",
726         searchTerms => "'$search'",
727         searchSort => "'$sort'",
728         searchSortDir => "'$sort_dir'",
729         searchLang => "'$lang'",
730     );
731
732     return $new_doc->toString(1); 
733 }
734
735 package OpenILS::WWW::SuperCat::Feed::htmlcard::item;
736 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
737
738 package OpenILS::WWW::SuperCat::Feed::htmlholdings;
739 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard';
740
741 sub new {
742     my $class = shift;
743     my $self = $class->SUPER::new;
744     $self->{xsl} = "/MARC21slim2HTMLCard-holdings.xsl";
745     return $self;
746 }
747
748 package OpenILS::WWW::SuperCat::Feed::htmlholdings::item;
749 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard::item';
750
751
752 package OpenILS::WWW::SuperCat::Feed::marctxt;
753 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
754
755 sub new {
756     my $class = shift;
757     my $self = $class->SUPER::new;
758     $self->{type} = 'text/plain';
759     $self->{xsl} = "/MARC21slim2MARCtxt.xsl";
760     return $self;
761 }
762
763
764 our ($_parser, $_xslt, $xslt_file);
765
766 sub toString {
767     my $self = shift;
768     my $base = $self->base || '';
769     my $root = $self->root || '';
770     my $search = $self->search || '';
771     my $class = $self->class || '';
772     my $lib = $self->lib || '-';
773
774     $self->composeDoc;
775
776         $_parser ||= new XML::LibXML;
777         $_xslt ||= new XML::LibXSLT;
778
779     $xslt_file ||=
780                 OpenSRF::Utils::SettingsClient
781                         ->new
782                         ->config_value( dirs => 'xsl' ).
783                 $self->{xsl};
784
785         # parse the MARC text xslt ...
786         my $marctxt_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
787
788     my $new_doc = $marctxt_xslt->transform(
789         $self->{doc},
790         base_dir => "'$root'",
791         lib => "'$lib'",
792         searchTerms => "'$search'",
793         searchClass => "'$class'",
794     );
795
796     return $marctxt_xslt->output_as_bytes($new_doc);
797 }
798
799
800 package OpenILS::WWW::SuperCat::Feed::marctxt::item;
801 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
802
803
804 package OpenILS::WWW::SuperCat::Feed::ris;
805 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
806
807 sub new {
808     my $class = shift;
809     my $self = $class->SUPER::new;
810     $self->{type} = 'text/plain';
811     $self->{xsl} = "/MARC21slim2RIS.xsl";
812     return $self;
813 }
814
815
816 our ($_parser, $_xslt, $xslt_file);
817
818 sub toString {
819     my $self = shift;
820     my $base = $self->base || '';
821     my $root = $self->root || '';
822     my $search = $self->search || '';
823     my $class = $self->class || '';
824     my $lib = $self->lib || '-';
825
826     $self->composeDoc;
827
828         $_parser ||= new XML::LibXML;
829         $_xslt ||= new XML::LibXSLT;
830
831     $xslt_file ||=
832                 OpenSRF::Utils::SettingsClient
833                         ->new
834                         ->config_value( dirs => 'xsl' ).
835                 $self->{xsl};
836
837         # parse the MARC text xslt ...
838         my $ris_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
839
840     my $new_doc = $ris_xslt->transform(
841         $self->{doc},
842         base_dir => "'$root'",
843         lib => "'$lib'",
844         searchTerms => "'$search'",
845         searchClass => "'$class'",
846     );
847
848     return $ris_xslt->output_as_bytes($new_doc);
849 }
850
851
852 package OpenILS::WWW::SuperCat::Feed::ris::item;
853 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
854
855 package OpenILS::WWW::SuperCat::Feed::marc21;
856 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
857 use MARC::File::XML ( BinaryEncoding => 'utf8', RecordFormat => 'USMARC' );
858
859 sub new {
860     my $class = shift;
861     my $self = $class->SUPER::new;
862     $self->{type} = 'application/octet-stream';
863     return $self;
864 }
865
866
867 sub toString {
868     my $self = shift;
869
870     $self->{doc} = '';
871     for my $item ( $self->items ) {
872         my $r = MARC::Record->new_from_xml( $item->{doc}->toString(1) );
873         $self->{doc} .= $r->as_usmarc;
874     }
875
876     utf8::encode($self->{doc});
877     return $self->{doc};
878 }
879
880 package OpenILS::WWW::SuperCat::Feed::marc21::item;
881 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
882
883
884 1;