From 7cc5b20f6e52c43b722721ce85b61d4020a95955 Mon Sep 17 00:00:00 2001 From: miker Date: Mon, 6 Mar 2006 20:17:26 +0000 Subject: [PATCH] refactoring to generic "feeds"; adding opensearch target git-svn-id: svn://svn.open-ils.org/ILS/trunk@3271 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm | 455 +++++------------- .../src/perlmods/OpenILS/WWW/SuperCat/Feed.pm | 328 +++++++++++++ 2 files changed, 447 insertions(+), 336 deletions(-) create mode 100644 Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm b/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm index da97a972de..2c5dbc0723 100644 --- a/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm +++ b/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm @@ -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(''); - $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(''); - $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(''); - $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(''); - $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 index 0000000000..6edae27ba5 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat/Feed.pm @@ -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(''); + $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(''); + $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(''); + $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(''); + $self->{type} = 'html'; + $self->{item_xpath} = '/html/body'; + return $self; +} + + +1; -- 2.43.2