#!/usr/bin/perl # catalog.cgi - a simple library catalog based on the content of a MyLibrary instance # Eric Lease Morgan # August 21, 2007 - added about page # July 19, 2007 - added "borrowing" and reading of reviews; i should call it "cooked" # July 18, 2007 - added paging of SRU results; started adding check-out features # July 5, 2007 - first investigations # require/use use CGI; use CGI::Carp qw(fatalsToBrowser); use LWP::UserAgent; use MyLibrary::Core; use MyLibrary::Patron; use MyLibrary::Auth::Basic; use MARC::Batch; use MARC::File::XML ( BinaryEncoding => 'utf8', RecordFormat => 'USMARC' ); use strict; use XML::LibXML; use XML::LibXSLT; # define constants use constant TEMPLATE => 'etc/template.txt'; use constant HOME => 'etc/home.txt'; use constant RESULTS => 'etc/results.txt'; use constant ABOUT => 'etc/about.txt'; use constant TAGGED => 'etc/tagged.txt'; use constant SRUXSLT => 'etc/style.xsl'; use constant MARC2MODS => 'etc/marc2mods.xsl'; use constant PAGESIZE => 25; use constant SRUROOT => 'http://dewey.library.nd.edu/mylibrary/demos/catalog/sru/server.cgi?operation=searchRetrieve&version=1.1&startRecord=##START##&maximumRecords=##PAGESIZE##&query='; use constant FILENAME => 'Local file'; use constant ISBN => 'ISBN'; use constant LCCN => 'LCCN'; use constant NOART => 'etc/no-art.jpeg'; use constant CMDTERM => 'term'; use constant FORMSMALL => 'etc/form-small.txt'; use constant AMAZON => 'http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=xyzzy&Operation=ItemLookup&ResponseGroup=Images,Reviews&ItemId='; use constant AMAZON2PICT => 'etc/amazon2pict.xsl'; use constant AMAZON2REVW => 'etc/amazon2reviews.xsl'; use constant CREATE => 'etc/create.txt'; use constant SIGNIN => 'etc/signin.txt'; use constant COOKIENAME => 'catalog'; use constant EXPIRE => '60m'; use constant CHOICES_A => 'etc/choices-authenticated.txt'; use constant CHOICES_N => 'etc/choices-not.txt'; use constant ERROR => 'etc/error.txt'; # initialize my $cgi = CGI->new; my $html; MyLibrary::Config->instance( 'catalog' ); # authenticate, if there is a cookie my ( $auth, $session_id, $status, $username ); if ( $cgi->cookie( COOKIENAME )) { $session_id = $cgi->cookie( COOKIENAME ); $auth = MyLibrary::Auth::Basic->new( sessid => $session_id, session_expire => EXPIRE ); $status = $auth->status; $username = $auth->username; } # get the command my $cmd = $cgi->param( 'cmd' ); # branch accordingly if (! $cmd ) { # display the home page $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( HOME )/e; $html =~ s/##FORMSMALL##//e; } elsif ( $cmd eq 'about' ) { # display the home page $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ABOUT )/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } elsif ( $cmd eq 'reviews' ) { # get the input my $id = $cgi->param( 'id' ); # make sure there is input if ( ! $id ) { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( 'No id was supplied. Call Eric.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # sanity check elsif ( ! MyLibrary::Resource->new( id => $id )) { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( "The supplied resource ID ($id) does not exist. Call Eric." ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # do the work else { # get the resource and isbn my $resource = MyLibrary::Resource->new( id => $id ); my $isbn = &get_location( $resource, ISBN ); my $reviews; if ( ! $isbn ) { # display an error my $title = $cgi->h1( 'Bummer' ); my $message = $cgi->p( "The supplied resource ID ($id) does not have an ISBN number and therefore I can't get a review." ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } else { my $ua = LWP::UserAgent->new( agent => 'AMAZON-Images-Client/0.1 ' ); my $request = HTTP::Request->new( GET => AMAZON . $isbn ); my $response = $ua->request( $request ); # create parser and process input if ($response->is_success) { my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ); my $style = $parser->parse_file( AMAZON2REVW ); my $stylesheet = $xslt->parse_stylesheet( $style ); my $results = $stylesheet->transform( $source ); $reviews = $cgi->ol( $stylesheet->output_string( $results )) unless ( ! $stylesheet->output_string( $results )); $reviews =~ s/<//g; } # done $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$cgi->h1('Reviews for ' . $resource->name)/e; $html =~ s/##MESSAGE##/$reviews/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } } } elsif ( $cmd eq 'checkout' ) { # get the input my $id = $cgi->param( 'id' ); # make sure they are authenticated if ( $status ne 'authenticated' ) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'You need to be authenticated in order to check out an item. Please ' . $cgi->a({ href => './?cmd=signin' }, 'sign in' ) . '.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # make sure there is input elsif ( ! $id ) { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( 'No id was supplied. Call Eric.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # sanity check elsif ( ! MyLibrary::Resource->new( id => $id )) { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( "The supplied resource ID ($id) does not exist. Call Eric." ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # do the work else { # get this patron and add this item to their pile my $patron = MyLibrary::Patron->new( username => $username ); my $resource = MyLibrary::Resource->new( id => $id ); my $name = $resource->name; $patron->patron_resources( new => [ $id ]); $patron->commit; # display the result my $username = $auth->username; my $title = $cgi->h1( 'Success!' ); my $message = $cgi->p( "Your item has been identified. A student has retrieved the item ($name) and finished delivering it to your door. You might want to now view " . $cgi->a({ href => './?cmd=my' }, 'your borrowed items' ) . '.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } } elsif ( $cmd eq 'return' ) { # get the input my $id = $cgi->param( 'id' ); my $confirm = $cgi->param( 'confirm' ); # check for id if ( ! $id ) { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( 'This function requires a resource ID as input. Call Eric.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # make sure they are authenticatead elsif ( $status ne 'authenticated' ) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'You need to be authenticated in order to return something from your bookshelf. Please ' . $cgi->a({ href => './?cmd=signin' }, 'sign in' ) . '.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # make sure the passed id is valid elsif ( ! MyLibrary::Resource->new( id => $id )) { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( "The supplied resource ID ($id) does not point to a valid resource. Call Eric." ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # confirm the deletion elsif ( ! $confirm ) { # return a confirmation my $resource = MyLibrary::Resource->new( id => $id ); my $name = $resource->name; my $title = $cgi->h1( 'Are you sure?' ); my $message = $cgi->p( "Are you sure you want to return $name to the library? " . $cgi->a({ href => './?cmd=return&id=' . $resource->id . '&confirm=yes' }, 'Yes') . ', return it. ' . $cgi->a({ href => './?cmd=my' }, 'No' ) . ', never mind.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # do the work elsif ( $confirm eq 'yes' ) { # get this patron and update their resource list my $patron = MyLibrary::Patron->new( username => $username ); $patron->patron_resources( del => [ $id ]); $patron->commit; # display the results my $title = $cgi->h1( 'Done' ); my $message = $cgi->p( "The title has been returned to the library. Now, wasn't that easy? You might now want to view " . $cgi->a({ href => './?cmd=my' }, 'your list' ) . '.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } } elsif ( $cmd eq 'my' ) { # make sure they are authenticated if ( $status ne 'authenticated' ) { # display an error my $title = $cgi->h1( 'Bummer' ); my $message = $cgi->p( 'You need to be signed in to use this function. ' . $cgi->a({ href => './?cmd=signin' }, 'Sign in' ) . '.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # do the work else { # create the patron my $patron = MyLibrary::Patron->new( username => $username ); my $reading_list; foreach ( $patron->patron_resources( sort => 'name' )) { my $resource = MyLibrary::Resource->new( id => $_ ); $reading_list .= $cgi->li( $resource->name . ' (' . $cgi->a({ href => './?cmd=return&id=' . $resource->id }, 'return' ) . ')' ); } # sanity check if ( $reading_list ) { $reading_list = $cgi->ol( $reading_list )} else { $reading_list = $cgi->p( '(Your bookshelf is empty.)' )} # display the list, or lack thereof my $title = $cgi->h1( "Bookshelf for $username" ); my $message = $cgi->p( 'Here is a list of things checked out to you:' ); $message .= $reading_list; $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } } elsif ( $cmd eq 'create' ) { # get the input my $username = $cgi->param( 'username' ); my $password = $cgi->param( 'password' ); my $echo = $cgi->param( 'echo' ); # sanity check if ( ! $username or ! $password ) { # display an error $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( CREATE )/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # more sanity checking elsif ( ! $username or ! $password or ! $echo ) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'Alas, you need to complete all three fields. Please go back and try again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # even more checking elsif ( $password ne $echo ) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'Bummer, your password and echoed password do not match. Please go back and try again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # make sure the name isn't already taken elsif ( MyLibrary::Patron->new( username => $username )) { # display an error my $title = $cgi->h1( 'Something went wrong' ); my $message = $cgi->p( 'Hmmm, that username has already been take. Please try another.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # whew! else { # do the work my $patron = MyLibrary::Patron->new; $patron->patron_username( $username ); $patron->patron_password( $password ); $patron->patron_stylesheet_id( 1 ); $patron->commit; # display the result my $title = $cgi->h1( 'Congrats!' ); my $message = $cgi->p( "The account named $username has been created. Now, please " . $cgi->a({ href => './?cmd=signin' }, 'sign in' ) . '.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } } elsif ( $cmd eq 'signin' ) { # get the input my $username = $cgi->param( 'username' ); my $password = $cgi->param( 'password' ); # make sure they aren't already logged in if ( $status eq 'authenticated' ) { # display an error my $username = $auth->username; my $title = $cgi->h1( 'Oops!' ); my $message = $cgi->p( "It seems as if you are already signed in as $username. If this is not you, then please " . $cgi->a({ href => './?cmd=signout' }, 'sign out' ) . ' and sign in again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # display the form elsif ( ! $username or ! $password ) { # display form $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( SIGNIN )/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # do the work else { # authenticate; my $auth = MyLibrary::Auth::Basic->new( session_expire => EXPIRE ); my $return_code = $auth->authenticate( username => $username, password => $password ); # cool if ( $return_code eq 'success' ) { # set the cookie $session_id = $auth->sessid; $status = $auth->status; $username = $auth->username; my $cookie = $cgi->cookie( -name => COOKIENAME, -value => $session_id ); my $header = $cgi->header( -cookie => $cookie, -type => 'text/html', -charset => 'utf-8' ); # set up the result my $title = $cgi->h1( "Hello, $username" ); my $message = $cgi->p( 'You have sucessfully signed in. Welcome.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##STATUS##/$status/ge; $html =~ s/##USERNAME##/$username/ge; $html =~ s/##SESSIONID##/$session_id/ge; $html =~ s/##CHOICES##/&choices/ge; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; # send the cookie, the html, and quit print $header; print $html; exit; } # bad input elsif ( $return_code eq 'password failure' or $return_code eq 'username failure' ) { # display an error my $title = $cgi->h1( 'Username/password problem' ); my $message = $cgi->p( 'That username/password combination is invalid. Please try again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } # unknown error else { # display an error my $title = $cgi->h1( 'Software error' ); my $message = $cgi->p( "Unknown value for return_code ($return_code). Call Eric." ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; } } } elsif ( $cmd eq 'signout' ) { # do the work, immediately my $cookie = $cgi->cookie( -name => COOKIENAME, -value => '', -expires => '-1h' ); my $header = $cgi->header( -cookie => $cookie, -type => 'text/html', -charset => 'utf-8' ); $auth->close_session; # re-define $session_id = ''; $status = ''; $username = ''; # set up the result my $title = $cgi->h1( 'Sign out' ); my $message = $cgi->p( 'You have successfully signed out. Thank you for being with us. ' . $cgi->a({ href => './?cmd=signin' } , 'Sign in' ) . ' again.' ); $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( ERROR )/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; $html =~ s/##TITLE##/$title/e; $html =~ s/##MESSAGE##/$message/e; $html =~ s/##STATUS##/$status/ge; $html =~ s/##USERNAME##/$username/ge; $html =~ s/##SESSIONID##/$session_id/ge; $html =~ s/##CHOICES##/&choices/ge; # reset the cookie, send the html, and quit, immediately print $header; print $html; exit; } elsif ( $cmd eq 'tagged' ) { # get the resource my $resource = MyLibrary::Resource->new( id => $cgi->param( 'id' )); # get this record; not done elegantly my $batch = MARC::Batch->new( 'USMARC', &get_location( $resource, FILENAME ) ); my $marc = $batch->next; # display the results page $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( TAGGED )/e; $html =~ s/##TAGGED##/$marc->as_formatted/e; } elsif ( $cmd eq 'marcxml' ) { # get the resource my $resource = MyLibrary::Resource->new( id => $cgi->param( 'id' )); # get this record; not done elegantly my $batch = MARC::Batch->new( 'USMARC', &get_location( $resource, FILENAME ) ); my $marc = $batch->next; # display & done print $cgi->header( -type => 'text/xml', -charset => 'utf-8' ); print MARC::File::XML::record( $marc ); exit; } elsif ( $cmd eq 'mods' ) { # get the resource my $resource = MyLibrary::Resource->new( id => $cgi->param( 'id' )); # get this record; not done elegantly my $batch = MARC::Batch->new( 'USMARC', &get_location( $resource, FILENAME ) ); my $marc = $batch->next; # transform marcxml into mods my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( MARC::File::XML::record( $marc )) or croak $!; my $style = $parser->parse_file( MARC2MODS ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; # display & done print $cgi->header( -type => 'text/xml', -charset => 'utf-8' ); print $stylesheet->output_string( $results ); exit; } elsif ( $cmd eq 'search' ) { # get the input my $query = $cgi->param( 'query' ); my $start = $cgi->param( 'start' ); # sanity check if ( ! $start ) { $start = 0 } # munge the query into cql while ( $query =~ /^ / ) { $query =~ s/^ // } while ( $query =~ / $/ ) { $query =~ s/ $// } while ( $query =~ / / ) { $query =~ s/ / / } if ($query =~ /\s/) { if (( $query =~ / and / ) | ( $query =~ / or / ) | ( $query =~ / not / )) { } elsif ( $query =~ /=/ ) { } elsif ( $query !~ /"/ ) { # try to make queries with no syntactical sugar a bit "smarter" my @terms = split / /, $query; my $enhancement; for ( my $i; $i <= $#terms; $i++ ) { if ( $i < $#terms ) { $enhancement .= $terms[$i] . ' and ' } else { $enhancement .= $terms[$i] } } $query = '"' . $query . '"' . " or ($enhancement)"; } } # create an SRU URL my $url = SRUROOT . "$query"; $url =~ s/##PAGESIZE##/PAGESIZE/e; $url =~ s/##START##/$start/e; # create a user agent, create a request, send it, and get a response my $ua = LWP::UserAgent->new( agent => 'SRU-Client/0.1 ' ); my $request = HTTP::Request->new( GET => $url ); my $response = $ua->request( $request ); # transform the response into a snippet of html my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ) or croak $!; my $style = $parser->parse_file( SRUXSLT ) or croak $!; my $stylesheet = $xslt->parse_stylesheet( $style ) or croak $!; my $results = $stylesheet->transform( $source ) or croak $!; # create a list of integers from the xslt; note, the first integer is the total number of records my $ids = $stylesheet->output_string( $results ); chop $ids; my @ids = split /\t/, $ids; my $number_of_hits = splice @ids, 0, 1; # build the hit list my $hit_list; if ( $number_of_hits ) { for ( my $i = 0; $i <= $#ids; $i++ ) { $hit_list .= &list_one_resource( $ids[ $i ]); last if ( $i == $number_of_hits ); } $hit_list = $cgi->ol({ -start => $start + 1 }, $hit_list ); } # display the results page $html = &slurp( TEMPLATE ); $html =~ s/##CONTENT##/&slurp( RESULTS )/e; $html =~ s/##FORMSMALL##/&slurp( FORMSMALL )/e; $html =~ s/##QUERY##/$cgi->param( 'query' )/ge; $html =~ s/##NUMBEROFHITS##/&commify( $number_of_hits )/e; $html =~ s/##HITLIST##/$hit_list/e; $html =~ s/##PAGER##/&pager( '?cmd=search&query=' . $cgi->param( 'query' ) . '&start=', $start, $number_of_hits )/e; } else { # error $html = $cgi->start_html( -title => 'MyLibrary' ); $html .= $cgi->h1( 'MyLibrary' ); $html .= $cgi->p( "Unknown value for cmd ($cmd). Call Eric." ); $html .= $cgi->end_html; } # done $html =~ s/##STATUS##/$status/ge; $html =~ s/##USERNAME##/$username/ge; $html =~ s/##SESSIONID##/$session_id/ge; $html =~ s/##CHOICES##/&choices/ge; print $cgi->header( -type => 'text/html', -charset => 'utf-8' ); print $html; exit; ############# # subroutines sub slurp { # open a file named by the input and return its contents my $f = @_[0]; my $r; open (F, "< $f"); while () { $r .= $_ } close F; return $r; } sub list_one_resource { # get this resouce my $resource = MyLibrary::Resource->new( id => shift ); # initialize my $cells; my $rows; my $details; # get the isbn my $isbn = &get_location( $resource, ISBN ); # initialize cover art and rating my $image_source = NOART; my $rating = ''; if ( $isbn ) { my $ua = LWP::UserAgent->new( agent => 'AMAZON-Images-Client/0.1 ' ); my $request = HTTP::Request->new( GET => AMAZON . $isbn ); my $response = $ua->request( $request ); # create parser and process input if ($response->is_success) { my $parser = XML::LibXML->new; my $xslt = XML::LibXSLT->new; my $source = $parser->parse_string( $response->content ); my $style = $parser->parse_file( AMAZON2PICT ); my $stylesheet = $xslt->parse_stylesheet( $style ); my $results = $stylesheet->transform( $source ); my $amazon = $stylesheet->output_string( $results ) unless ( ! $stylesheet->output_string( $results )); my @amazon = split /\t/, $amazon; $image_source = @amazon[0] unless ( ! @amazon[0] ); $rating = @amazon[1] . ' stars out of ' . @amazon[2] . ' reviews ' . $cgi->a({ href => './?cmd=reviews&id=' . $resource->id }, 'Read reviews') unless ( ! @amazon[1] ); } } # add rating if ( $rating ) { $cells = $cgi->td({ -class => 'label' }, 'rating: ' ); $cells .= $cgi->td( $rating ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # add isbn to output if ( $isbn ) { $cells = $cgi->td({ -class => 'label' }, 'ISBN: ' ); $cells .= $cgi->td( $isbn ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # view as $cells = $cgi->td({ -class => 'label' }, 'view as: ' ); $cells .= $cgi->td( $cgi->a({ -href => './?cmd=tagged&id=' . $resource->id }, 'tagged' ) . '; ' . $cgi->a({ -href => './?cmd=marcxml&id=' . $resource->id }, 'MARCXML' ) . '; ' . $cgi->a({ -href => './?cmd=mods&id=' . $resource->id }, 'MODS' ) ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); # call number my $lccn = &get_location( $resource, LCCN ); if ( $lccn ) { $cells = $cgi->td({ -class => 'label' }, 'location: ' ); $cells .= $cgi->td( $lccn ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # status $cells = $cgi->td({ -class => 'label' }, 'status: ' ); $cells .= $cgi->td( 'available ' . $cgi->a({ href => './?cmd=checkout&id=' . $resource->id }, 'Get it for me!') ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); # creator if ( $resource->creator ) { $cells = $cgi->td({ -class => 'label' }, 'creator(s): ' ); $cells .= $cgi->td( $resource->creator ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # description if ( $resource->note ) { $cells = $cgi->td({ -class => 'label' }, 'note(s): ' ); $cells .= $cgi->td( $resource->note ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # subjects if ( $resource->subject ) { $cells = $cgi->td({ -class => 'label' }, 'subject(s): ' ); $cells .= $cgi->td( $resource->subject ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # format if ( $resource->format ) { $cells = $cgi->td({ -class => 'label' }, 'format: ' ); $cells .= $cgi->td( $resource->format ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # source if ( $resource->source ) { $cells = $cgi->td({ -class => 'label' }, 'source: ' ); $cells .= $cgi->td( $resource->source ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # type if ( $resource->type ) { $cells = $cgi->td({ -class => 'label' }, 'type: ' ); $cells .= $cgi->td( $resource->type ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # language if ( $resource->language ) { $cells = $cgi->td({ -class => 'label' }, 'language: ' ); $cells .= $cgi->td( $resource->language ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # coverage if ( $resource->coverage ) { $cells = $cgi->td({ -class => 'label' }, 'coverage: ' ); $cells .= $cgi->td( $resource->coverage ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # relation if ( $resource->relation ) { $cells = $cgi->td({ -class => 'label' }, 'relation: ' ); $cells .= $cgi->td( $resource->relation ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # publisher if ( $resource->publisher ) { $cells = $cgi->td({ -class => 'label' }, 'publisher: ' ); $cells .= $cgi->td( $resource->publisher ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # rights if ( $resource->rights ) { $cells = $cgi->td({ -class => 'label' }, 'rights: ' ); $cells .= $cgi->td( $resource->rights ); $rows .= $cgi->Tr( { -valign => 'top' }, $cells ); } # wrap the details in a table $details = $cgi->table({ -border => 0, -class => 'detail' }, $rows ); # add cover art $cells = $cgi->td({ -class => 'label' }, $cgi->img({ -src => $image_source, -height => 60, -width => 44 })); $cells .= $cgi->td( $details ); $rows = $cgi->Tr( { -valign => 'top' }, $cells ); # wrap the whole thing in a table as well as a div $details = $cgi->table({ -border => 0 }, $rows ); $details = $cgi->div({ -id => 'd' . $resource->id, -style => 'display: block' }, $details ); # done; complete with cool show/hide javascript #return $cgi->li({ -class => 'citation' }, $resource->name . ' ' . $cgi->a( { -href => "javascript:expand('d" . $resource->id . "')", -style => 'color: grey; text-decoration: none' }, 'more...' ) . $details ); return $cgi->li({ -class => 'citation' }, $resource->name . $details ); } sub commify { # cool hack; http://www.unix.org.ua/orelly/perl/cookbook/ch02_18.htm my $text = reverse $_[0]; $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; return scalar reverse $text; } sub pager { # get the input my $link = shift; my $start = shift; my $size = shift; # create the links my $previous = $cgi->a({ -href => $link . ( $start - PAGESIZE ) }, 'Previous' ) . '  '; my $next = '  ' . $cgi->a({ -href => $link . ( $start + PAGESIZE ) }, 'Next' ); # build the pager my $pager; if ( $start - PAGESIZE >= 0 ) { $pager = $previous } if ( $start + PAGESIZE < $size ) { $pager .= $next } # done return $pager; } sub get_location { # get the input my $resource = shift; my $location_type = shift; # initialize my $location; # process each location foreach ( $resource->resource_locations ) { my $type = MyLibrary::Resource::Location::Type->new( id => $_->resource_location_type ); if ( $type->name eq $location_type ) { $location = $_->location; last; } } # done return $location } sub facet_term_combinations { # get the input my @facets = @_; # initialize my $terms; foreach ( @facets ) { # create a list of terms associated with each facet foreach my $id ($_->related_terms(sort => 'name')) { my $term = MyLibrary::Term->new( id => $id ); $terms .= '
  • ' . $term->term_name . ' (' . &commify( scalar( $term->related_resources )) . ' items)
  • '; } } # done return $cgi->ol( $terms ); } sub choices { # return a set of menu options if ( $status eq 'authenticated' ) { return &slurp( CHOICES_A )} else { return &slurp( CHOICES_N )} }