refactoring to generic "feeds"; adding opensearch target
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Mon, 6 Mar 2006 20:17:26 +0000 (20:17 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Mon, 6 Mar 2006 20:17:26 +0000 (20:17 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@3271 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm
Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm [new file with mode: 0644]

index da97a97..2c5dbc0 100644 (file)
@@ -19,10 +19,11 @@ use XML::LibXML;
 
 use Unicode::Normalize;
 use OpenILS::Utils::Fieldmapper;
+use OpenILS::WWW::SuperCat::Feed;
 
 
 # set the bootstrap config when this module is loaded
-my ($bootstrap, $supercat, $actor, $parser);
+my ($bootstrap, $supercat, $actor, $parser, $search);
 
 sub import {
        my $self = shift;
@@ -34,6 +35,7 @@ sub child_init {
        OpenSRF::System->bootstrap_client( config_file => $bootstrap );
        $supercat = OpenSRF::AppSession->create('open-ils.supercat');
        $actor = OpenSRF::AppSession->create('open-ils.actor');
+       $search = OpenSRF::AppSession->create('open-ils.search');
        $parser = new XML::LibXML;
 }
 
@@ -289,6 +291,7 @@ sub bookbag_feed {
 
        my $host = $cgi->virtual_host || $cgi->server_name;
        my $path = $apache->path_info;
+       my $base = $cgi->url;
 
        my ($id,$type) = reverse split '/', $path;
 
@@ -305,389 +308,169 @@ sub bookbag_feed {
        $feed->creator($host);
        $feed->update_ts(gmtime_ISO8601());
 
-       $feed->link(atom => $id);
-       $feed->link(rss2 => $id);
-       $feed->link(html => $id);
+       $feed->link(atom => $base . "/bookbag/atom/$id");
+       $feed->link(rss2 => $base . "/bookbag/rss2/$id");
+       $feed->link(html => $base . "/bookbag/html/$id");
 
        print entityize($feed->toString) . "\n";
 
        return Apache2::Const::OK;
 }
 
-sub create_record_feed {
-       my $type = shift;
-       my $records = shift;
-       my $unapi = shift;
+sub opensearch_feed {
+       my $apache = shift;
+       return Apache2::Const::DECLINED if (-e $apache->filename);
+
+       print "Content-type: application/xml; charset=utf-8\n\n";
 
        my $cgi = new CGI;
-       my $base = $cgi->url;
-       my $host = $cgi->virtual_host || $cgi->server_name;
+       (my $unapi = $cgi->url) =~ s{[^/]+/?$}{unapi};
 
        my $year = (gmtime())[5];
 
-       my $feed = new OpenILS::WWW::SuperCat::Feed ($type);
-       $feed->base($base);
-       $feed->unapi($unapi);
-
-       for my $rec (@$records) {
-               my $item_tag = "tag:$host,$year:biblio-record_entry/" . $rec;
-
-               my $xml = $supercat->request(
-                       "open-ils.supercat.record.$type.retrieve",
-                       $rec
-               )->gather(1);
-
-               my $node = $feed->add_item($xml);
-
-               $node->id($item_tag);
-               $node->link(unapi => $item_tag);
-       }
-
-       return $feed;
-}
-
-sub entityize {
-       my $stuff = NFC(shift());
-       $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
-       return $stuff;
-}
-
-package OpenILS::WWW::SuperCat::Feed;
-
-sub new {
-       my $class = shift;
-       my $type = shift;
-       if ($type) {
-               $class .= '::'.$type;
-               return $class->new;
-       }
-       throw OpenSRF::EX::ERROR ("I need a feed type!") ;
-}
-
-sub build {
-       my $class = shift;
-       my $xml = shift;
-
-       my $self = { doc => $parser->parse_string($xml), items => [] };
-
-       return bless $self => $class;
-}
-
-sub base {
-       my $self = shift;
-       my $base = shift;
-       $self->{base} = $base if ($base);
-       return $self->{base};
-}
-
-sub unapi {
-       my $self = shift;
-       my $unapi = shift;
-       $self->{unapi} = $unapi if ($unapi);
-       return $self->{unapi};
-}
-
-sub push_item {
-       my $self = shift;
-       push @{ $self->{items} }, @_;
-}
-
-sub items {
-       my $self = shift;
-       return @{ $self->{items} } if (wantarray);
-       return $self->{items};
-}
-
-sub _add_node {
-       my $self = shift;
+       my $host = $cgi->virtual_host || $cgi->server_name;
+       my $base = $cgi->url;
+       my $path = $apache->path_info;
 
-       my $xpath = shift;
-       my $new = shift;
+       my $page = $cgi->param('startPage') || 1;
+       my $offset = $cgi->param('startIndex') || 1;
+       my $limit = $cgi->param('count') || 10;
 
-       for my $node ($self->{doc}->findnodes($xpath)) {
-               $node->appendChild($new);
-               last;
+       if ($page > 1) {
+               $offset = ($page - 1) * $limit;
+       } else {
+               $offset -= 1;
        }
-}
 
-sub _create_node {
-       my $self = shift;
-
-       my $xpath = shift;
-       my $ns = shift;
-       my $name = shift;
-       my $text = shift;
-       my $attrs = shift;
-
-       for my $node ($self->{doc}->findnodes($xpath)) {
-               my $new = $self->{doc}->createElement($name) if (!$ns);
-               $new = $self->{doc}->createElementNS($ns,$name) if ($ns);
-
-               $new->appendChild( $self->{doc}->createTextNode( $text ) )
-                       if ($text);
-
-               if (ref($attrs)) {
-                       for my $key (keys %$attrs) {
-                               $new->setAttribute( $key => $$attrs{$key} );
-                       }
-               }
-
-               $node->appendChild( $new );
+       my ($terms,$class,$type,$version) = reverse split '/', $path;
 
-               return $new;
+       if ($version eq '1.0') {
+               $type = 'rss2';
+       } elsif ($type eq '-') {
+               $type = 'atom';
        }
-}
 
-sub add_item {
-       my $self = shift;
-       my $class = ref($self) || $self;
-       $class .= '::item';
-
-       my $item_xml = shift;
-       my $entry = $class->new($item_xml);
+       $class = 'keyword' if ($class eq '-');
+       $terms =~ s/\+/ /go;
 
-       $entry->base($self->base);
-       $entry->unapi($self->unapi);
+       warn "searching for $class -> [$terms] via OS $version, response type $type";
 
-       $self->push_item($entry);
-       return $entry;
-}
-
-sub toString {
-       my $self = shift;
-       for my $root ( $self->{doc}->findnodes($self->{item_xpath}) ) {
-               for my $item ( $self->items ) {
-                       $root->appendChild( $item->{doc}->documentElement );
+       my $recs = $search->request(
+               'open-ils.search.biblio.record.class.search' => $class,
+               { term          => $terms,
+                 org_unit      => 1,
+                 limit         => $limit,
+                 offset        => $offset,
                }
-               last;
-       }
-
-       return $self->{doc}->toString;
-}
-
-sub id {};
-sub link {};
-sub title {};
-sub update_ts {};
-sub creator {};
+       )->gather(1);
 
-#----------------------------------------------------------
-
-package OpenILS::WWW::SuperCat::Feed::atom;
-use base 'OpenILS::WWW::SuperCat::Feed';
-
-sub new {
-       my $class = shift;
-       my $self = $class->SUPER::build('<atom:feed xmlns:atom="http://www.w3.org/2005/Atom"/>');
-       $self->{type} = 'atom';
-       $self->{item_xpath} = '/atom:feed';
-       return $self;
-}
-
-sub title {
-       my $self = shift;
-       my $text = shift;
-       $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:title', $text);
-}
-
-sub update_ts {
-       my $self = shift;
-       my $text = shift;
-       $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:updated', $text);
-}
-
-sub creator {
-       my $self = shift;
-       my $text = shift;
-       $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:author');
-       $self->_create_node('/atom:feed/atom:author', 'http://www.w3.org/2005/Atom','atom:name', $text);
-}
-
-sub link {
-       my $self = shift;
-       my $type = shift;
-       my $id = shift;
-
-       $self->_create_node(
-               '/atom:feed',
-               'http://www.w3.org/2005/Atom',
-               'atom:link',
-               undef,
-               { rel => $type,
-                 href => $self->base . '/' . $type . '/' . $id,
-                 type => "application/$type+xml",
-               }
+       my $feed = create_record_feed(
+               $type,
+               [ map { $_->[0] } @{$recs->{ids}} ],
+               $unapi,
        );
-}
 
-sub id {
-       my $self = shift;
-       my $id = shift;
-
-       $self->_create_node( '/atom:feed', 'http://www.w3.org/2005/Atom', 'atom:id', $id );
-}
-
-package OpenILS::WWW::SuperCat::Feed::atom::item;
-use base 'OpenILS::WWW::SuperCat::Feed::atom';
-
-sub new {
-       my $class = shift;
-       my $xml = shift;
-       my $self = $class->SUPER::build($xml);
-       $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom');
-       $self->{type} = 'atom::item';
-       return $self;
-}
-
-sub link {
-       my $self = shift;
-       my $type = shift;
-       my $id = shift;
-
-       if ($type eq 'unapi') {
-               $self->_create_node(
-                       'atom:entry',
-                       'http://www.w3.org/2005/Atom',
-                       'atom:link',
-                       undef,
-                       { rel => $type,
-                         type => "application/xml",
-                         href => $self->unapi . '?uri=' . $id,
-                       }
-               );
-       }
-}
+=head
+       my $bucket = $actor->request("open-ils.actor.container.public.flesh", 'biblio', $id)->gather(1);
+       my $bucket_tag = "tag:$host,$year:record_bucket/$id";
 
+       my $feed = create_record_feed(
+               $type,
+               [ map { $_->target_biblio_record_entry } @{ $bucket->items } ],
+               $unapi,
+       );
 
-#----------------------------------------------------------
+       $feed->title("Items in Book Bag #".$bucket->id);
+       $feed->creator($host);
+       $feed->update_ts(gmtime_ISO8601());
 
-package OpenILS::WWW::SuperCat::Feed::rss2;
-use base 'OpenILS::WWW::SuperCat::Feed';
+       $feed->link(atom => $id);
+       $feed->link(rss2 => $id);
+       $feed->link(html => $id);
 
-sub new {
-       my $class = shift;
-       my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
-       $self->{type} = 'rss2';
-       $self->{item_xpath} = '/rss/channel';
-       return $self;
-}
+=cut
 
-sub title {
-       my $self = shift;
-       my $text = shift;
-       $self->_create_node('/rss/channel',undef,'title', $text);
-}
+       $feed->_create_node(
+               $feed->{item_xpath},
+               'http://a9.com/-/spec/opensearch/1.1/',
+               'totalResults',
+               $recs->{count},
+       );
 
-sub update_ts {
-       my $self = shift;
-       my $text = shift;
-       $self->_create_node('/rss/channel',undef,'lastBuildDate', $text);
-}
+       $feed->_create_node(
+               $feed->{item_xpath},
+               'http://a9.com/-/spec/opensearch/1.1/',
+               'startIndex',
+               $offset + 1,
+       );
 
-sub creator {
-       my $self = shift;
-       my $text = shift;
-       $self->_create_node('/rss/channel', undef,'generator', $text);
-}
+       $feed->_create_node(
+               $feed->{item_xpath},
+               'http://a9.com/-/spec/opensearch/1.1/',
+               'itemsPerPage',
+               $limit,
+       );
 
-sub link {
-       my $self = shift;
-       my $type = shift;
-       my $id = shift;
-
-       $self->_create_node(
-               '/rss/channel',
-               undef,
-               'link',
-               $self->base . '/' . $type . '/' . $id,
-               { rel => $type }
+       $feed->link(
+               next =>
+               $base . $path . "?startIndex=" . int($offset + $limit + 1) . "&count=" . $limit =>
+               'application/opensearch+xml'
+       ) if ($offset + $limit < $recs->{count});
+
+       $feed->link(
+               prev =>
+               $base . $path . "?startIndex=" . int(($offset - $limit) + 1) . "&count=" . $limit =>
+               'application/opensearch+xml'
+       ) if ($offset);
+
+       $feed->link(
+               self =>
+               $base . $path =>
+               'application/opensearch+xml'
        );
-}
 
-package OpenILS::WWW::SuperCat::Feed::rss2::item;
-use base 'OpenILS::WWW::SuperCat::Feed::rss2';
 
-sub new {
-       my $class = shift;
-       my $xml = shift;
-       my $self = $class->SUPER::build($xml);
-       $self->{type} = 'atom::item';
-       return $self;
+       print entityize($feed->toString) . "\n";
+       return Apache2::Const::OK;
 }
 
-sub link {
-       my $self = shift;
+sub create_record_feed {
        my $type = shift;
-       my $id = shift;
-
-       $self->_create_node( item => undef, 'link' => $self->unapi . '?uri=' . $id )
-               if ($type eq 'unapi');
-}
-
+       my $records = shift;
+       my $unapi = shift;
 
-#----------------------------------------------------------
+       my $cgi = new CGI;
+       my $base = $cgi->url;
+       my $host = $cgi->virtual_host || $cgi->server_name;
 
-package OpenILS::WWW::SuperCat::Feed::mods;
-use base 'OpenILS::WWW::SuperCat::Feed';
+       my $year = (gmtime())[5];
 
-sub new {
-       my $class = shift;
-       my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/"/>');
-       $self->{type} = 'mods';
-       $self->{item_xpath} = '/mods:modsCollection';
-       return $self;
-}
+       my $feed = new OpenILS::WWW::SuperCat::Feed ($type);
+       $feed->base($base);
+       $feed->unapi($unapi);
 
-package OpenILS::WWW::SuperCat::Feed::mods::item;
-use base 'OpenILS::WWW::SuperCat::Feed::mods';
+       for my $rec (@$records) {
+               my $item_tag = "tag:$host,$year:biblio-record_entry/" . $rec;
 
-sub new {
-       my $class = shift;
-       my $xml = shift;
-       my $self = $class->SUPER::build($xml);
-       $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', 'mods');
-       $self->{type} = 'mods::item';
-       return $self;
-}
+               my $xml = $supercat->request(
+                       "open-ils.supercat.record.$type.retrieve",
+                       $rec
+               )->gather(1);
 
-my $linkid = 1;
+               my $node = $feed->add_item($xml);
 
-sub link {
-       my $self = shift;
-       my $type = shift;
-       my $id = shift;
-
-       if ($type eq 'unapi') {
-               $self->_create_node(
-                       'mods:mods',
-                       'http://www.loc.gov/mods/',
-                       'mods:relatedItem',
-                       undef,
-                       { type => 'otherFormat', id => 'link-'.$linkid }
-               );
-               $self->_create_node(
-                       "mods:mods/mods:relatedItem[\@id='link-$linkid']",
-                       'http://www.loc.gov/mods/',
-                       'mods:recordIdentifier',
-                       $self->unapi .'?uri=' . $id
-               );
-               $linkid++;
+               $node->id($item_tag);
+               $node->link(opac => $feed->unapi . "?uri=$item_tag&format=opac");
+               $node->link(unapi => $feed->unapi . "?uri=$item_tag");
        }
-}
-
 
-#----------------------------------------------------------
-
-package OpenILS::WWW::SuperCat::Feed::html;
-use base 'OpenILS::WWW::SuperCat::Feed';
-
-sub new {
-       my $class = shift;
-       my $self = $class->SUPER::build('<html><head/><body/></html>');
-       $self->{type} = 'html';
-       $self->{item_xpath} = '/html/body';
-       return $self;
+       return $feed;
 }
 
+sub entityize {
+       my $stuff = NFC(shift());
+       $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
+       return $stuff;
+}
 
 1;
diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm b/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm
new file mode 100644 (file)
index 0000000..6edae27
--- /dev/null
@@ -0,0 +1,328 @@
+package OpenILS::WWW::SuperCat::Feed;
+use strict; use warnings;
+use vars qw/$parser/;
+use OpenSRF::EX qw(:try);
+use XML::LibXML;
+use CGI;
+
+sub new {
+       my $class = shift;
+       my $type = shift;
+       if ($type) {
+               $class .= '::'.$type;
+               return $class->new;
+       }
+       throw OpenSRF::EX::ERROR ("I need a feed type!") ;
+}
+
+sub build {
+       my $class = shift;
+       my $xml = shift;
+
+       $parser = new XML::LibXML if (!$parser);
+
+       my $self = { doc => $parser->parse_string($xml), items => [] };
+
+       return bless $self => $class;
+}
+
+sub base {
+       my $self = shift;
+       my $base = shift;
+       $self->{base} = $base if ($base);
+       return $self->{base};
+}
+
+sub unapi {
+       my $self = shift;
+       my $unapi = shift;
+       $self->{unapi} = $unapi if ($unapi);
+       return $self->{unapi};
+}
+
+sub push_item {
+       my $self = shift;
+       push @{ $self->{items} }, @_;
+}
+
+sub items {
+       my $self = shift;
+       return @{ $self->{items} } if (wantarray);
+       return $self->{items};
+}
+
+sub _add_node {
+       my $self = shift;
+
+       my $xpath = shift;
+       my $new = shift;
+
+       for my $node ($self->{doc}->findnodes($xpath)) {
+               $node->appendChild($new);
+               last;
+       }
+}
+
+sub _create_node {
+       my $self = shift;
+
+       my $xpath = shift;
+       my $ns = shift;
+       my $name = shift;
+       my $text = shift;
+       my $attrs = shift;
+
+       for my $node ($self->{doc}->findnodes($xpath)) {
+               my $new = $self->{doc}->createElement($name) if (!$ns);
+               $new = $self->{doc}->createElementNS($ns,$name) if ($ns);
+
+               $new->appendChild( $self->{doc}->createTextNode( $text ) )
+                       if (defined $text);
+
+               if (ref($attrs)) {
+                       for my $key (keys %$attrs) {
+                               $new->setAttribute( $key => $$attrs{$key} );
+                       }
+               }
+
+               $node->appendChild( $new );
+
+               return $new;
+       }
+}
+
+sub add_item {
+       my $self = shift;
+       my $class = ref($self) || $self;
+       $class .= '::item';
+
+       my $item_xml = shift;
+       my $entry = $class->new($item_xml);
+
+       $entry->base($self->base);
+       $entry->unapi($self->unapi);
+
+       $self->push_item($entry);
+       return $entry;
+}
+
+sub toString {
+       my $self = shift;
+       for my $root ( $self->{doc}->findnodes($self->{item_xpath}) ) {
+               for my $item ( $self->items ) {
+                       $root->appendChild( $item->{doc}->documentElement );
+               }
+               last;
+       }
+
+       return $self->{doc}->toString(1);
+}
+
+sub id {};
+sub link {};
+sub title {};
+sub update_ts {};
+sub creator {};
+
+#----------------------------------------------------------
+
+package OpenILS::WWW::SuperCat::Feed::atom;
+use base 'OpenILS::WWW::SuperCat::Feed';
+
+sub new {
+       my $class = shift;
+       my $self = $class->SUPER::build('<atom:feed xmlns:atom="http://www.w3.org/2005/Atom"/>');
+       $self->{type} = 'atom';
+       $self->{item_xpath} = '/atom:feed';
+       return $self;
+}
+
+sub title {
+       my $self = shift;
+       my $text = shift;
+       $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:title', $text);
+}
+
+sub update_ts {
+       my $self = shift;
+       my $text = shift;
+       $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:updated', $text);
+}
+
+sub creator {
+       my $self = shift;
+       my $text = shift;
+       $self->_create_node('/atom:feed','http://www.w3.org/2005/Atom','atom:author');
+       $self->_create_node('/atom:feed/atom:author', 'http://www.w3.org/2005/Atom','atom:name', $text);
+}
+
+sub link {
+       my $self = shift;
+       my $type = shift;
+       my $id = shift;
+       my $mime = shift || "application/$type+xml";
+
+       $type = 'self' if ($type eq 'atom');
+
+       $self->_create_node(
+               $self->{item_xpath},
+               'http://www.w3.org/2005/Atom',
+               'atom:link',
+               undef,
+               { rel => $type,
+                 href => $id,
+                 type => $mime,
+               }
+       );
+}
+
+sub id {
+       my $self = shift;
+       my $id = shift;
+
+       $self->_create_node( '/atom:feed', 'http://www.w3.org/2005/Atom', 'atom:id', $id );
+}
+
+package OpenILS::WWW::SuperCat::Feed::atom::item;
+use base 'OpenILS::WWW::SuperCat::Feed::atom';
+
+sub new {
+       my $class = shift;
+       my $xml = shift;
+       my $self = $class->SUPER::build($xml);
+       $self->{doc}->documentElement->setNamespace('http://www.w3.org/2005/Atom', 'atom');
+       $self->{item_xpath} = '/atom:entry';
+       $self->{type} = 'atom::item';
+       return $self;
+}
+
+
+#----------------------------------------------------------
+
+package OpenILS::WWW::SuperCat::Feed::rss2;
+use base 'OpenILS::WWW::SuperCat::Feed';
+
+sub new {
+       my $class = shift;
+       my $self = $class->SUPER::build('<rss version="2.0"><channel/></rss>');
+       $self->{type} = 'rss2';
+       $self->{item_xpath} = '/rss/channel';
+       return $self;
+}
+
+sub title {
+       my $self = shift;
+       my $text = shift;
+       $self->_create_node('/rss/channel',undef,'title', $text);
+}
+
+sub update_ts {
+       my $self = shift;
+       my $text = shift;
+       $self->_create_node('/rss/channel',undef,'lastBuildDate', $text);
+}
+
+sub creator {
+       my $self = shift;
+       my $text = shift;
+       $self->_create_node('/rss/channel', undef,'generator', $text);
+}
+
+sub link {
+       my $self = shift;
+       my $type = shift;
+       my $id = shift;
+       my $mime = shift || "application/$type+xml";
+
+       $type = 'self' if ($type eq 'rss2');
+
+       $self->_create_node(
+               $self->{item_xpath},
+               undef,
+               'link',
+               $id,
+               { rel => $type,
+                 type => $mime,
+               }
+       );
+}
+
+package OpenILS::WWW::SuperCat::Feed::rss2::item;
+use base 'OpenILS::WWW::SuperCat::Feed::rss2';
+
+sub new {
+       my $class = shift;
+       my $xml = shift;
+       my $self = $class->SUPER::build($xml);
+       $self->{type} = 'atom::item';
+       $self->{item_xpath} = '/item';
+       return $self;
+}
+
+
+#----------------------------------------------------------
+
+package OpenILS::WWW::SuperCat::Feed::mods;
+use base 'OpenILS::WWW::SuperCat::Feed';
+
+sub new {
+       my $class = shift;
+       my $self = $class->SUPER::build('<mods:modsCollection version="3.0" xmlns:mods="http://www.loc.gov/mods/"/>');
+       $self->{type} = 'mods';
+       $self->{item_xpath} = '/mods:modsCollection';
+       return $self;
+}
+
+package OpenILS::WWW::SuperCat::Feed::mods::item;
+use base 'OpenILS::WWW::SuperCat::Feed::mods';
+
+sub new {
+       my $class = shift;
+       my $xml = shift;
+       my $self = $class->SUPER::build($xml);
+       $self->{doc}->documentElement->setNamespace('http://www.loc.gov/mods/', 'mods');
+       $self->{type} = 'mods::item';
+       return $self;
+}
+
+my $linkid = 1;
+
+sub link {
+       my $self = shift;
+       my $type = shift;
+       my $id = shift;
+
+       if ($type eq 'unapi' || $type eq 'opac') {
+               $self->_create_node(
+                       'mods:mods',
+                       'http://www.loc.gov/mods/',
+                       'mods:relatedItem',
+                       undef,
+                       { type => 'otherFormat', id => 'link-'.$linkid }
+               );
+               $self->_create_node(
+                       "mods:mods/mods:relatedItem[\@id='link-$linkid']",
+                       'http://www.loc.gov/mods/',
+                       'mods:recordIdentifier',
+                       $id
+               );
+               $linkid++;
+       }
+}
+
+
+#----------------------------------------------------------
+
+package OpenILS::WWW::SuperCat::Feed::html;
+use base 'OpenILS::WWW::SuperCat::Feed';
+
+sub new {
+       my $class = shift;
+       my $self = $class->SUPER::build('<html><head/><body/></html>');
+       $self->{type} = 'html';
+       $self->{item_xpath} = '/html/body';
+       return $self;
+}
+
+
+1;