1 package OpenILS::WWW::SuperCat::Feed;
2 use strict; use warnings;
4 use OpenSRF::EX qw(:try);
7 use OpenSRF::Utils::SettingsClient;
10 use DateTime::Format::Mail;
17 return 1 if UNIVERSAL::can("OpenILS::WWW::SuperCat::Feed::$type" => 'new');
28 throw OpenSRF::EX::ERROR ("I need a feed type!") ;
34 return undef unless $xml;
36 $parser = new XML::LibXML if (!$parser);
38 my $self = { doc => $parser->parse_string($xml), items => [] };
40 $self = bless $self => $class;
48 $self->{type} = $type if ($type);
54 return $self->{count};
60 $self->{search} = $search if ($search);
61 return $self->{search};
67 $self->{class} = $search if ($search);
68 return $self->{class};
74 $self->{sort} = $search if ($search);
81 $self->{sort_dir} = $search if ($search);
82 return $self->{sort_dir};
88 $self->{lang} = $search if ($search);
95 $self->{lib} = $lib if ($lib);
102 $self->{base} = $base if ($base);
103 return $self->{base};
109 $self->{root} = $root if ($root);
110 return $self->{root};
116 $self->{unapi} = $unapi if ($unapi);
117 return $self->{unapi};
122 $self->{count} += scalar(@_);
123 push @{ $self->{items} }, @_;
128 return @{ $self->{items} } if (wantarray);
129 return $self->{items};
138 for my $node ($self->{doc}->findnodes($xpath)) {
139 $node->appendChild($new);
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);
157 $new->appendChild( $self->{doc}->createTextNode( $text ) )
161 for my $key (keys %$attrs) {
162 next unless $$attrs{$key};
163 $new->setAttribute( $key => $$attrs{$key} );
167 $node->appendChild( $new );
175 my $class = ref($self) || $self;
178 my $item_xml = shift;
179 my $entry = $class->new($item_xml);
180 return undef unless $entry;
182 $entry->base($self->base);
183 $entry->unapi($self->unapi);
185 $self->push_item($entry);
191 my $holdings_xml = shift;
193 return $self unless ($holdings_xml);
195 $parser = new XML::LibXML if (!$parser);
196 my $new_doc = $parser->parse_string($holdings_xml);
198 for my $root ( $self->{doc}->findnodes($self->{holdings_xpath}) ) {
199 $root->appendChild($new_doc->documentElement);
207 for my $root ( $self->{doc}->findnodes($self->{item_xpath}) ) {
208 for my $item ( $self->items ) {
209 $root->appendChild( $item->{doc}->documentElement );
218 return $self->{doc}->toString(1);
228 #----------------------------------------------------------
230 package OpenILS::WWW::SuperCat::Feed::atom;
231 use base 'OpenILS::WWW::SuperCat::Feed';
232 use OpenSRF::Utils qw/:datetime/;
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';
247 $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','title', $text);
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);
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);
268 my $mime = shift || "application/x-$type+xml";
271 $type = 'self' if ($type eq 'atom');
275 'http://www.w3.org/2005/Atom',
290 $self->_create_node( $self->{item_xpath}, 'http://www.w3.org/2005/Atom', 'id', $id );
293 package OpenILS::WWW::SuperCat::Feed::atom::item;
294 use base 'OpenILS::WWW::SuperCat::Feed::atom';
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';
309 #----------------------------------------------------------
311 package OpenILS::WWW::SuperCat::Feed::rss2;
312 use base 'OpenILS::WWW::SuperCat::Feed';
316 my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
317 $self->{type} = 'application/rss+xml';
318 $self->{item_xpath} = '/rss/channel';
325 $self->_create_node('/rss/channel',undef,'title', $text);
331 $self->_create_node('/rss/channel',undef,'description', $text);
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);
344 $self->_create_node('/rss/channel', undef,'generator', $text);
351 my $mime = shift || "application/x-$type+xml";
353 if ($type eq 'rss2' or $type eq 'alternate') {
354 # Just link to ourself using standard RSS2 link element
363 # Alternate link: use XHTML link element
366 'http://www.w3.org/1999/xhtml',
380 $self->_create_node($self->{item_xpath}, undef,'guid', $id);
383 package OpenILS::WWW::SuperCat::Feed::rss2::item;
384 use base 'OpenILS::WWW::SuperCat::Feed::rss2';
389 my $self = $class->SUPER::build($xml);
390 $self->{type} = 'application/rss+xml';
391 $self->{item_xpath} = '/item';
392 $self->{holdings_xpath} = '/item';
398 # RSS2 demands RFC-822 compliant datetime formats
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);
408 $self->_create_node($self->{item_xpath},undef,'pubDate', $text);
426 #----------------------------------------------------------
428 package OpenILS::WWW::SuperCat::Feed::mods;
429 use base 'OpenILS::WWW::SuperCat::Feed';
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';
439 package OpenILS::WWW::SuperCat::Feed::mods::item;
440 use base 'OpenILS::WWW::SuperCat::Feed::mods';
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';
460 if ($type eq 'unapi' || $type eq 'opac') {
466 { type => 'otherFormat', id => 'link-'.$linkid }
469 "mods:mods/relatedItem[\@id='link-$linkid']",
478 #----------------------------------------------------------
480 package OpenILS::WWW::SuperCat::Feed::mods3;
481 use base 'OpenILS::WWW::SuperCat::Feed::mods';
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';
491 package OpenILS::WWW::SuperCat::Feed::mods3::item;
492 use base 'OpenILS::WWW::SuperCat::Feed::mods::item';
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';
510 if ($type eq 'unapi' || $type eq 'opac') {
516 { type => 'otherFormat', id => 'link-'.$linkid }
519 "mods:mods/relatedItem[\@id='link-$linkid']",
529 #----------------------------------------------------------
531 package OpenILS::WWW::SuperCat::Feed::mods32;
532 use base 'OpenILS::WWW::SuperCat::Feed::mods3';
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';
542 package OpenILS::WWW::SuperCat::Feed::mods32::item;
543 use base 'OpenILS::WWW::SuperCat::Feed::mods3::item';
545 #----------------------------------------------------------
547 package OpenILS::WWW::SuperCat::Feed::mods33;
548 use base 'OpenILS::WWW::SuperCat::Feed::mods3';
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';
558 package OpenILS::WWW::SuperCat::Feed::mods33::item;
559 use base 'OpenILS::WWW::SuperCat::Feed::mods3::item';
562 #----------------------------------------------------------
564 package OpenILS::WWW::SuperCat::Feed::marcxml;
565 use base 'OpenILS::WWW::SuperCat::Feed';
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';
579 if ($type eq 'unapi') {
582 'http://www.w3.org/1999/xhtml',
585 { rel => 'unapi-server', href => $id, title => "unapi" }
592 package OpenILS::WWW::SuperCat::Feed::marcxml::item;
593 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
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"]';
611 if ($type eq 'opac') {
613 '*[local-name()="record"]',
614 'http://www.w3.org/1999/xhtml',
617 { rel => 'otherFormat', href => $id, title => "Dynamic Details" }
620 } elsif ($type eq 'unapi-id') {
622 '*[local-name()="record"]',
623 'http://www.w3.org/1999/xhtml',
626 { title => $id, class => "unapi-id" }
633 #----------------------------------------------------------
635 package OpenILS::WWW::SuperCat::Feed::html;
636 use base 'OpenILS::WWW::SuperCat::Feed::atom';
640 my $self = $class->SUPER::new;
641 $self->type('text/html');
645 our ($_parser, $_xslt, $xslt_file);
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 || '-';
657 $_parser ||= new XML::LibXML;
658 $_xslt ||= new XML::LibXSLT;
661 OpenSRF::Utils::SettingsClient
663 ->config_value( dirs => 'xsl' ).
666 # parse the MODS xslt ...
667 my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
669 my $new_doc = $atom2html_xslt->transform(
671 base_dir => "'$root'",
673 searchTerms => "'$search'",
674 searchClass => "'$class'",
677 return $new_doc->toString(1);
681 package OpenILS::WWW::SuperCat::Feed::html::item;
682 use base 'OpenILS::WWW::SuperCat::Feed::atom::item';
684 #----------------------------------------------------------
686 package OpenILS::WWW::SuperCat::Feed::htmlcard;
687 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
691 my $self = $class->SUPER::new;
692 $self->type('text/html');
693 $self->{xsl} = "/MARC21slim2HTMLCard.xsl";
697 our ($_parser, $_xslt, $xslt_file);
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 || '-';
711 $_parser ||= new XML::LibXML;
712 $_xslt ||= new XML::LibXSLT;
715 OpenSRF::Utils::SettingsClient
717 ->config_value( dirs => 'xsl' ).$self->{xsl};
719 # parse the MODS xslt ...
720 my $atom2html_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
722 my $new_doc = $atom2html_xslt->transform(
724 base_dir => "'$root'",
726 searchTerms => "'$search'",
727 searchSort => "'$sort'",
728 searchSortDir => "'$sort_dir'",
729 searchLang => "'$lang'",
732 return $new_doc->toString(1);
735 package OpenILS::WWW::SuperCat::Feed::htmlcard::item;
736 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
738 package OpenILS::WWW::SuperCat::Feed::htmlholdings;
739 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard';
743 my $self = $class->SUPER::new;
744 $self->{xsl} = "/MARC21slim2HTMLCard-holdings.xsl";
748 package OpenILS::WWW::SuperCat::Feed::htmlholdings::item;
749 use base 'OpenILS::WWW::SuperCat::Feed::htmlcard::item';
752 package OpenILS::WWW::SuperCat::Feed::marctxt;
753 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
757 my $self = $class->SUPER::new;
758 $self->{type} = 'text/plain';
759 $self->{xsl} = "/MARC21slim2MARCtxt.xsl";
764 our ($_parser, $_xslt, $xslt_file);
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 || '-';
776 $_parser ||= new XML::LibXML;
777 $_xslt ||= new XML::LibXSLT;
780 OpenSRF::Utils::SettingsClient
782 ->config_value( dirs => 'xsl' ).
785 # parse the MARC text xslt ...
786 my $marctxt_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
788 my $new_doc = $marctxt_xslt->transform(
790 base_dir => "'$root'",
792 searchTerms => "'$search'",
793 searchClass => "'$class'",
796 return $marctxt_xslt->output_string($new_doc);
800 package OpenILS::WWW::SuperCat::Feed::marctxt::item;
801 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
804 package OpenILS::WWW::SuperCat::Feed::ris;
805 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
809 my $self = $class->SUPER::new;
810 $self->{type} = 'text/plain';
811 $self->{xsl} = "/MARC21slim2RIS.xsl";
816 our ($_parser, $_xslt, $xslt_file);
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 || '-';
828 $_parser ||= new XML::LibXML;
829 $_xslt ||= new XML::LibXSLT;
832 OpenSRF::Utils::SettingsClient
834 ->config_value( dirs => 'xsl' ).
837 # parse the MARC text xslt ...
838 my $ris_xslt = $_xslt->parse_stylesheet( $_parser->parse_file($xslt_file) );
840 my $new_doc = $ris_xslt->transform(
842 base_dir => "'$root'",
844 searchTerms => "'$search'",
845 searchClass => "'$class'",
848 return $ris_xslt->output_string($new_doc);
852 package OpenILS::WWW::SuperCat::Feed::ris::item;
853 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';
855 package OpenILS::WWW::SuperCat::Feed::marc21;
856 use base 'OpenILS::WWW::SuperCat::Feed::marcxml';
857 use MARC::File::XML ( BinaryEncoding => 'utf8', RecordFormat => 'USMARC' );
861 my $self = $class->SUPER::new;
862 $self->{type} = 'application/octet-stream';
871 for my $item ( $self->items ) {
872 my $r = MARC::Record->new_from_xml( $item->{doc}->toString(1) );
873 $self->{doc} .= $r->as_usmarc;
876 utf8::encode($self->{doc});
880 package OpenILS::WWW::SuperCat::Feed::marc21::item;
881 use base 'OpenILS::WWW::SuperCat::Feed::marcxml::item';