--- /dev/null
+package OpenILS::WWW::EGCatLoader;
+use strict; use warnings;
+use CGI;
+use XML::LibXML;
+use Digest::MD5 qw(md5_hex);
+use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR REDIRECT);
+use OpenSRF::AppSession;
+use OpenSRF::Utils::Logger qw/$logger/;
+use OpenILS::Application::AppUtils;
+use OpenILS::Utils::CStoreEditor qw/:funcs/;
+use OpenILS::Utils::Fieldmapper;
+my $U = 'OpenILS::Application::AppUtils';
+
+sub new {
+ my($class, $apache, $ctx) = @_;
+
+ my $self = bless({}, ref($class) || $class);
+
+ $self->apache($apache);
+ $self->ctx($ctx);
+ $self->cgi(CGI->new);
+
+ OpenILS::Utils::CStoreEditor->init; # just in case
+ $self->editor(new_editor());
+
+ return $self;
+}
+
+
+# current Apache2::RequestRec;
+sub apache {
+ my($self, $apache) = @_;
+ $self->{apache} = $apache if $apache;
+ return $self->{apache};
+}
+
+# runtime context
+sub ctx {
+ my($self, $ctx) = @_;
+ $self->{ctx} = $ctx if $ctx;
+ return $self->{ctx};
+}
+
+# cstore editor
+sub editor {
+ my($self, $editor) = @_;
+ $self->{editor} = $editor if $editor;
+ return $self->{editor};
+}
+
+# CGI handle
+sub cgi {
+ my($self, $cgi) = @_;
+ $self->{cgi} = $cgi if $cgi;
+ return $self->{cgi};
+}
+
+
+# load common data, then load page data
+sub load {
+ my $self = shift;
+
+ my $path = $self->apache->path_info;
+ $self->load_helpers;
+
+ my $stat = $self->load_common;
+ return $stat unless $stat == Apache2::Const::OK;
+
+ return $self->load_home if $path =~ /opac\/home/;
+ return $self->load_login if $path =~ /opac\/login/;
+ return $self->load_logout if $path =~ /opac\/logout/;
+ return $self->load_rresults if $path =~ /opac\/results/;
+ return $self->load_rdetail if $path =~ /opac\/rdetail/;
+ return $self->load_myopac if $path =~ /opac\/myopac/;
+ return $self->load_place_hold if $path =~ /opac\/place_hold/;
+
+ return Apache2::Const::OK;
+}
+
+# general purpose utility functions added to the environment
+# context additions:
+# find_org_unit : function(id) => aou object
+# org_tree : function(id) => aou object, top of tree, fleshed
+my $cached_org_tree;
+my %org_unit_map;
+sub load_helpers {
+ my $self = shift;
+
+ # pull the org unit from the cached org tree
+ $self->ctx->{find_org_unit} = sub {
+ my $org_id = shift;
+ return undef unless defined $org_id;
+ return $org_unit_map{$org_id} if defined $org_unit_map{$org_id};
+ my $tree = shift || $self->ctx->{org_tree}->();
+ return $org_unit_map{$org_id} = $tree if $tree->id == $org_id;
+ for my $child (@{$tree->children}) {
+ my $node = $self->ctx->{find_org_unit}->($org_id, $child);
+ return $node if $node;
+ }
+ return undef;
+ };
+
+ $self->ctx->{org_tree} = sub {
+ unless($cached_org_tree) {
+ $cached_org_tree = $self->editor->search_actor_org_unit([
+ { parent_ou => undef},
+ { flesh => -1,
+ flesh_fields => {aou => ['children', 'ou_type']},
+ order_by => {aou => 'name'}
+ }
+ ])->[0];
+ }
+ return $cached_org_tree;
+ }
+}
+
+# context additions:
+# authtoken : string
+# user : au object
+# user_status : hash of user circ numbers
+sub load_common {
+ my $self = shift;
+
+ my $e = $self->editor;
+ my $ctx = $self->ctx;
+
+ if($e->authtoken($self->cgi->cookie('ses'))) {
+
+ if($e->checkauth) {
+
+ $ctx->{authtoken} = $e->authtoken;
+ $ctx->{user} = $e->requestor;
+ $ctx->{user_stats} = $U->simplereq(
+ 'open-ils.actor',
+ 'open-ils.actor.user.opac.vital_stats',
+ $e->authtoken, $e->requestor->id);
+
+ } else {
+
+ return $self->load_logout;
+ }
+ }
+
+ return Apache2::Const::OK;
+}
+
+sub load_home {
+ my $self = shift;
+ $self->ctx->{page} = 'home';
+ return Apache2::Const::OK;
+}
+
+
+sub load_login {
+ my $self = shift;
+ my $cgi = $self->cgi;
+
+ $self->ctx->{page} = 'login';
+
+ my $username = $cgi->param('username');
+ my $password = $cgi->param('password');
+
+ return Apache2::Const::OK unless $username and $password;
+
+ my $seed = $U->simplereq(
+ 'open-ils.auth',
+ 'open-ils.auth.authenticate.init',
+ $username);
+
+ my $response = $U->simplereq(
+ 'open-ils.auth',
+ 'open-ils.auth.authenticate.complete',
+ { username => $username,
+ password => md5_hex($seed . md5_hex($password)),
+ type => 'opac'
+ }
+ );
+
+ # XXX check event, redirect as necessary
+
+ my $home = $self->apache->unparsed_uri;
+ $home =~ s/\/login/\/home/;
+
+ $self->apache->print(
+ $cgi->redirect(
+ -url => $cgi->param('origin') || $home,
+ -cookie => $cgi->cookie(
+ -name => 'ses',
+ -path => '/',
+ -value => $response->{payload}->{authtoken},
+ -expires => CORE::time + $response->{payload}->{authtime}
+ )
+ )
+ );
+
+ return Apache2::Const::REDIRECT;
+}
+
+sub load_logout {
+ my $self = shift;
+
+ my $path = $self->apache->uri;
+ $path =~ s/(\/[^\/]+$)/\/home/;
+ my $url = 'http://' . $self->apache->hostname . "$path";
+
+ $self->apache->print(
+ $self->cgi->redirect(
+ -url => $url,
+ -cookie => $self->cgi->cookie(
+ -name => 'ses',
+ -path => '/',
+ -value => '',
+ -expires => '-1h'
+ )
+ )
+ );
+
+ return Apache2::Const::REDIRECT;
+}
+
+# context additions:
+# page_size
+# hit_count
+# records : list of bre's and copy-count objects
+my $cmf_cache;
+my $cmc_cache;
+sub load_rresults {
+ my $self = shift;
+
+ my $cgi = $self->cgi;
+ my $ctx = $self->ctx;
+ my $e = $self->editor;
+
+ $ctx->{page} = 'rresult';
+
+ unless($cmf_cache) {
+ $cmf_cache = $e->search_config_metabib_field({id => {'!=' => undef}});
+ $cmc_cache = $e->search_config_metabib_class({name => {'!=' => undef}});
+ $ctx->{metabib_field} = $cmf_cache;
+ $ctx->{metabib_class} = $cmc_cache;
+ }
+
+
+ my $page = $cgi->param('page') || 0;
+ my $query = $cgi->param('query');
+ my $limit = $cgi->param('limit') || 10; # XXX user settings
+
+ my $args = {limit => $limit, offset => $page * $limit};
+ my $results = $U->simplereq('open-ils.search',
+ 'open-ils.search.biblio.multiclass.query.staff', $args, $query, 1);
+
+ my $search = OpenSRF::AppSession->create('open-ils.search');
+ my $facet_req = $search->request('open-ils.search.facet_cache.retrieve', $results->{facet_key}, 10);
+
+ my $rec_ids = [map { $_->[0] } @{$results->{ids}}];
+
+ $ctx->{page_size} = $limit;
+ $ctx->{hit_count} = $results->{count};
+
+ my $cstore1 = OpenSRF::AppSession->create('open-ils.cstore');
+
+ my $bre_req = $cstore1->request(
+ 'open-ils.cstore.direct.biblio.record_entry.search', {id => $rec_ids});
+
+ my @data;
+ while(my $resp = $bre_req->recv) {
+ my $bre = $resp->content;
+
+ my $copy_counts = $e->json_query(
+ {from => ['asset.record_copy_count', 1, $bre->id, 0]})->[0];
+
+ push(@data,
+ {
+ bre => $bre,
+ marc_xml => XML::LibXML->new->parse_string($bre->marc),
+ copy_counts => $copy_counts
+ }
+ );
+ }
+
+ $cstore1->kill_me;
+
+ # shove recs into context in search results order
+ $ctx->{records} = [];
+ for my $rec_id (@$rec_ids) {
+ push(
+ @{$ctx->{records}},
+ grep { $_->{bre}->id == $rec_id } @data
+ );
+ }
+
+ my $facets = $facet_req->gather(1);
+
+ for my $cmf_id (keys %$facets) { # quick-n-dirty
+ my ($cmf) = grep { $_->id eq $cmf_id } @$cmf_cache;
+ $facets->{$cmf->label} = $facets->{$cmf_id};
+ delete $facets->{$cmf_id};
+ }
+ $ctx->{search_facets} = $facets;
+
+ return Apache2::Const::OK;
+}
+
+# context additions:
+# record : bre object
+sub load_rdetail {
+ my $self = shift;
+
+ $self->ctx->{record} = $self->editor->retrieve_biblio_record_entry([
+ $self->cgi->param('record'),
+ {
+ flesh => 2,
+ flesh_fields => {
+ bre => ['call_numbers'],
+ acn => ['copies'] # limit, paging, etc.
+ }
+ }
+ ]);
+
+ $self->ctx->{marc_xml} = XML::LibXML->new->parse_string($self->ctx->{record}->marc);
+
+ return Apache2::Const::OK;
+}
+
+# context additions:
+# user : au object, fleshed
+sub load_myopac {
+ my $self = shift;
+
+ $self->ctx->{user} = $self->editor->retrieve_actor_user([
+ $self->ctx->{user}->id,
+ {
+ flesh => 1,
+ flesh_fields => {
+ au => ['card']
+ # ...
+ }
+ }
+ ]);
+
+ return Apache2::Const::OK;
+}
+
+# context additions:
+sub load_place_hold {
+ my $self = shift;
+ my $ctx = $self->ctx;
+ my $e = $self->editor;
+
+ $ctx->{hold_target} = $self->cgi->param('hold_target');
+ $ctx->{hold_type} = $self->cgi->param('hold_type');
+ $ctx->{default_pickup_lib} = $e->requestor->home_ou; # XXX staff
+
+ if($ctx->{hold_type} eq 'T') {
+ $ctx->{record} = $e->retrieve_biblio_record_entry($ctx->{hold_target});
+ }
+ # ...
+
+ $ctx->{marc_xml} = XML::LibXML->new->parse_string($ctx->{record}->marc);
+
+ if(my $pickup_lib = $self->cgi->param('pickup_lib')) {
+
+ my $args = {
+ patronid => $e->requestor->id,
+ titleid => $ctx->{hold_target}, # XXX
+ pickup_lib => $pickup_lib,
+ depth => 0, # XXX
+ };
+
+ my $allowed = $U->simplereq(
+ 'open-ils.circ',
+ 'open-ils.circ.title_hold.is_possible',
+ $e->authtoken, $args
+ );
+
+ if($allowed->{success} == 1) {
+ my $hold = Fieldmapper::action::hold_request->new;
+
+ $hold->pickup_lib($pickup_lib);
+ $hold->requestor($e->requestor->id);
+ $hold->usr($e->requestor->id); # XXX staff
+ $hold->target($ctx->{hold_target});
+ $hold->hold_type($ctx->{hold_type});
+ # frozen, expired, etc..
+
+ my $stat = $U->simplereq(
+ 'open-ils.circ',
+ 'open-ils.circ.holds.create',
+ $e->authtoken, $hold
+ );
+
+ if($stat and $stat > 0) {
+ $ctx->{hold_success} = 1;
+ } else {
+ $ctx->{hold_failed} = 1; # XXX process the events, etc
+ }
+ }
+
+ # place the hold and deliver results
+ }
+
+ return Apache2::Const::OK;
+}
+
+1;
use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR);
use Apache2::Log;
use OpenSRF::EX qw(:try);
+use OpenILS::Utils::CStoreEditor;
use constant OILS_HTTP_COOKIE_SKIN => 'oils:skin';
use constant OILS_HTTP_COOKIE_THEME => 'oils:theme';
check_web_config($r); # option to disable this
my $ctx = load_context($r);
my $base = $ctx->{base_path};
+
+ $r->content_type('text/html; encoding=utf8');
+ my $stat = run_context_loader($r, $ctx);
+ return $stat unless $stat == Apache2::Const::OK;
+
my($template, $page_args, $as_xml) = find_template($r, $base, $ctx);
return Apache2::Const::DECLINED unless $template;
$template = $ctx->{skin} . "/$template";
$ctx->{page_args} = $page_args;
- $r->content_type('text/html; encoding=utf8');
my $tt = Template->new({
OUTPUT => ($as_xml) ? sub { parse_as_xml($r, $ctx, @_); } : $r,
INCLUDE_PATH => $ctx->{template_paths},
+ DEBUG => $ctx->{debug_template}
});
unless($tt->process($template, {ctx => $ctx})) {
return Apache2::Const::OK;
}
+
+sub run_context_loader {
+ my $r = shift;
+ my $ctx = shift;
+
+ my $stat = Apache2::Const::OK;
+
+ my $loader = $r->dir_config('OILSWebContextLoader');
+ return $stat unless $loader;
+
+ eval {
+ $loader->use;
+ $stat = $loader->new($r, $ctx)->load;
+ };
+
+ if($@) {
+ $r->log->error("Context Loader error: $@");
+ return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ $r->log->warn("context loader resulted in status $stat");
+ return $stat;
+}
+
sub parse_as_xml {
my $r = shift;
my $ctx = shift;
$data = $ctx->{final_dtd} . "\n" . $data;
$success = 1;
} otherwise {
- my $e = shift;
+ my $e = shift;
my $err = "Invalid XML: $e";
$r->log->error($err);
$r->content_type('text/plain; encoding=utf8');
sub load_context {
my $r = shift;
my $cgi = CGI->new;
- my $ctx = $web_config->{ctx};
+ my $ctx = {}; # new context for each page load
+ $ctx->{$_} = $web_config->{base_ctx}->{$_} for keys %{$web_config->{base_ctx}};
$ctx->{hostname} = $r->hostname;
$ctx->{base_url} = $cgi->url(-base => 1);
$ctx->{skin} = $cgi->cookie(OILS_HTTP_COOKIE_SKIN) || 'default';
$ctx->{base_path} = (ref $data->{base_path}) ? '' : $data->{base_path};
$ctx->{template_paths} = [];
$ctx->{force_valid_xml} = ($data->{force_valid_xml} =~ /true/io) ? 1 : 0;
+ $ctx->{debug_template} = ($data->{debug_template} =~ /true/io) ? 1 : 0;
$ctx->{default_template_extension} = $data->{default_template_extension} || 'tt2';
$ctx->{web_dir} = $data->{web_dir};
}
}
- return {ctx => $ctx, handlers => $handlers};
+ return {base_ctx => $ctx, handlers => $handlers};
}
package PathConfig;
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns='http://www.w3.org/1999/xhtml' lang='[% ctx.locale %]' xml:lang='[% ctx.locale %]'>
+ <head>
+ <title>[% ctx.page_title %]</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ [% BLOCK html_head; END; # provide a default that can be overridden %]
+ [% PROCESS html_head %]
+ </head>
+ <body>
+ [% content %]
+ </body>
+</html>
--- /dev/null
+[%
+ # Org Unit Selector Widget :
+ # PROCESS build_org_selector id='selector-id' name='selector-name'
+ BLOCK build_org_selector;
+ first_run = 0;
+ IF !org_unit;
+ org_unit = ctx.org_tree;
+ first_run = 1;
+%]
+ <select id='[% id %]' name='[% name %]'>
+ [% END %]
+ <option value='[% org_unit.id %]' [% IF org_unit.id == value %] selected='selected' [% END %]>
+ [%
+ pad = org_unit.ou_type.depth * 4;
+ FOR idx IN [0..pad]; ' '; END;
+ org_unit.name;
+ %]
+ </option>
+ [% FOR child IN org_unit.children; PROCESS build_org_selector org_unit = child; END %]
+ [% IF first_run %]
+ </select>
+ [% END %]
+[% END %]
+
+[% PROCESS 'default/opac/marc_attrs.tt2' %]
+
+
--- /dev/null
+[% ctx.page_title = "Home" %]
+
+[% BLOCK html_head %]
+<style>
+ #home_div { text-align: center; width: 100%; margin-top: 30px;}
+</style>
+[% END %]
+
+[% WRAPPER "default/opac/base.tt2" %]
+
+<div id='home_div'>
+ <img src='/images/eg_logo.jpg'/>
+ <br/><br/>
+ <form action='./results' method='GET'>
+ <input type='text' name='query' value='[% query %]'/>
+ <input type='submit'/>
+ <input type='hidden' name='page' value='0'/>
+ </form>
+</div>
+
+
+[% END %]
--- /dev/null
+[% BLOCK html_head %]
+<style>
+</style>
+[% END %]
+
+[%
+ USE CGI;
+ WRAPPER "default/opac/base.tt2";
+ ctx.page_title = "Login";
+%]
+
+<div style='width:400px; text-align:center; border: 1px solid #888'>
+ <form method='POST'>
+ <table>
+ <tr>
+ <td>Username or Barcode</td>
+ <td><input name='username' type='text'/></td>
+ </tr>
+ <tr>
+ <td>Password</td>
+ <td><input name='password' type='password'/></td>
+ </tr>
+ <tr>
+ <td colspan='2' style='text-align:center'>
+ <input type='submit'/>
+ </td>
+ </tr>
+ </table>
+ <input type='hidden' name='origin' value='[% CGI.param('origin') %]'/>
+ </form>
+</div>
+[% END %]
--- /dev/null
+[%
+ # Extract MARC fields from XML
+ # get_marc_attrs( { marc_xml => doc } )
+ BLOCK get_marc_attrs;
+ xml = args.marc_xml;
+ args.isbn = xml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
+ args.upc = xml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
+ args.issn = xml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
+ args.title = xml.findnodes('//*[@tag="245"]/*[@code="a"]').textContent;
+ args.author = xml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
+ args.publisher = xml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
+ args.pubdate = xml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
+
+ # clean up the ISBN
+ args.isbn_clean = args.isbn.replace('\ .*', '');
+ END;
+%]
--- /dev/null
+[% BLOCK html_head %]
+<style>
+ table { width: 100%; text-align: center; padding: 20px; margin-top: 30px; }
+ table { border-collapse: collapse; }
+ table { padding: 3px; border-bottom: 1px solid #ddd; text-align: left;}
+ table tr:nth-child(odd) { background-color:#ded; }
+</style>
+[% END %]
+
+[%
+ WRAPPER "default/opac/base.tt2";
+ ctx.page_title = "My Account";
+%]
+
+<table>
+ <tr>
+ <td>First Name</td>
+ <td>[% ctx.user.first_given_name %]</td>
+ </tr>
+ <tr>
+ <td>Middle Name</td>
+ <td>[% ctx.user.second_given_name %]</td>
+ </tr>
+ <tr>
+ <td>Last Name</td>
+ <td>[% ctx.user.family_name %]</td>
+ </tr>
+ <tr>
+ <td>Library Card</td>
+ <td>[% ctx.user.card.barcode %]</td>
+ </tr>
+ <tr>
+ <td>Email Address</td>
+ <td>[% ctx.user.email %]</td>
+ </tr>
+ <tr>
+ <td>Phone</td>
+ <td>[% ctx.user.day_phone %]</td>
+ </tr>
+</table>
+
+
+
+[% END %]
--- /dev/null
+[% BLOCK html_head %]
+<style>
+</style>
+[% END %]
+
+[%
+ USE CGI;
+ PROCESS "default/opac/common.tt2";
+ WRAPPER "default/opac/base.tt2";
+ ctx.page_title = "Place Hold";
+ attrs = {marc_xml => ctx.marc_xml};
+ PROCESS get_marc_attrs args=attrs;
+%]
+
+
+<div>
+ <div>Placing hold on [% attrs.title %], by [% attrs.author %]</div>
+ [% IF ctx.hold_success %]
+ <div>Succeeded</div>
+ [% ELSIF ctx.hold_failed %]
+ <div>Failed...</div>
+ [% ELSE %]
+ <form action='place_hold' method='POST'>
+ Choose a pickup Library [% PROCESS build_org_selector name='pickup_lib' value=ctx.default_pickup_lib %]
+ <input type='Submit'/>
+ <input type='hidden' name='hold_target' value='[% CGI.param('hold_target') %]'/>
+ <input type='hidden' name='hold_type' value='[% CGI.param('hold_type') %]'/>
+ </form>
+ [% END %]
+</div>
+
+[% END %]
--- /dev/null
+[% BLOCK html_head %]
+<style>
+ table { width: 100%; padding: 20px; margin-top: 30px; }
+ table { border-collapse: collapse; }
+ table td { padding: 3px; border-bottom: 1px solid #ddd; text-align: left;}
+ table th { padding: 3px; border-bottom: 1px solid #ddd; text-align: left;}
+ table tr:nth-child(even) { background-color:#ded; }
+ #record_table td { padding-left: 15px; padding-right: 15px; }
+</style>
+[% END %]
+
+[%
+ WRAPPER "default/opac/base.tt2";
+ PROCESS "default/opac/common.tt2";
+ ctx.page_title = "Details";
+ record = ctx.record;
+ attrs = {marc_xml => ctx.marc_xml};
+ PROCESS get_marc_attrs args=attrs;
+%]
+
+<div id='detail_div'>
+ <table id='record_table' style='width:auto'>
+ <tr>
+ <td rowspan='10' style='width:55px; vertical-align:top; padding-right:4px;'>
+ <img width='50' height='70' src='/opac/extras/ac/jacket/small/[% attrs.isbn_clean || attrs.upc %]'/>
+ </td>
+ </tr>
+ [% IF attrs.title %]<tr><td>Title</td><td>[% attrs.title %]</td></tr>[% END %]
+ [% IF attrs.author %]<tr><td>Author</td><td><a href='results?query=au:[% attrs.author | uri %]'>[% attrs.author %]</a></td></tr>[% END %]
+ [% IF attrs.isbn %]<tr><td>ISBN</td><td>[% attrs.isbn %]</td></tr>[% END %]
+ [% IF attrs.issn %]<tr><td>ISSN</td><td>[% attrs.issn %]</td></tr>[% END %]
+ [% IF attrs.upc %]<tr><td>UPC</td><td>[% attrs.upc %]</td></tr>[% END %]
+ [% IF attrs.pubdate %]<tr><td>Publication Date</td><td>[% attrs.pubdate %]</td></tr>[% END %]
+ [% IF attrs.publisher %]<tr><td>Publishere</td><td>[% attrs.publisher %]</td></tr>[% END %]
+ <tr>
+ <td>Subjects</td>
+ <td>
+ [% FOR node IN marc_xml.findnodes('//*[@tag="650"]') %]
+ [%
+ s0 = node.childNodes.0.textContent;
+ s1 = node.childNodes.1.textContent;
+ %]
+ [% IF s0 %]
+ <a href='results?query=su:[% s0 | url %]'>[% s0 %]</a>
+ [% IF s1 %]
+ <span>--</span>
+ <a href='results?query=su:[% s1 | url %]'>[% s1 %]</a>
+ [% END %]
+ <br/>
+ [% END %]
+ [% END %]
+ </td>
+ </tr>
+ </table>
+ <table id='copy_table'>
+ <thead>
+ <tr>
+ <th>Owning Lib</th>
+ <th>Call Number</th>
+ <th>Barcode</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOR acn IN record.call_numbers %]
+ [% FOR acp IN acn.copies %]
+ <tr>
+ <td>[% ctx.find_org_unit(acn.owning_lib).shortname %]</td>
+ <td>[% acn.label %]</td>
+ <td>[% acp.barcode %]</td>
+ <td>[% acp.status %]</td>
+ </tr>
+ [% END %]
+ [% END %]
+ </tbody>
+ </table>
+</div>
+
+
+[% END %]
--- /dev/null
+[% WRAPPER "default/opac/base.tt2" %]
+[% ctx.page_title = "Results" %]
+[% page = ctx.cgi.param('page') || 0; %]
+<a href='?page=[% page - 1 %]'>Prev</a> <a href='?page=[% page + 1 %]'>Next</a>
+<br/>
+
+<table>
+[%
+ e = ctx.editor;
+ idx = 0;
+ WHILE idx < 10;
+ id = idx + page * 10;
+ attrs = e.search_acq_lineitem_attr({lineitem => id});
+ isbn = '';
+ FOR attr IN attrs;
+ IF attr.attr_name == 'isbn';
+ isbn = attr.attr_value;
+ LAST;
+ END;
+ END;
+ idx = idx + 1;
+%]
+
+<tr>
+ <td><img width='65' height='90' src='/opac/extras/ac/jacket/small/[% isbn %]'/></td>
+ <td>[% FOR attr IN attrs; attr.attr_value _ ' / '; END; %]</td>
+</tr>
+
+ [% END %]
+</table>
+[% END %]
--- /dev/null
+[% BLOCK html_head %]
+<style>
+ #body_table { width: 100%; margin-top: 20px; }
+ #left_block { width: 15%; vertical-align: top; }
+ #right_block { width: auto; vertical-align: top;}
+ #record_table { border-collapse: collapse; width:100%; }
+ #record_table td { padding: 3px; border-bottom: 1px solid #ddd; }
+ #record_table tr:nth-child(odd) { background-color:#ded; }
+ #form_div { text-align: center; width: 100%; margin-top: 10px;}
+ #links_div { margin-bottom: 10px; padding: 5px;}
+</style>
+[% END %]
+
+[%
+ USE CGI;
+ USE POSIX;
+ WRAPPER "default/opac/base.tt2";
+ PROCESS "default/opac/common.tt2";
+ ctx.page_title = "Results";
+ page = CGI.param('page') || 0;
+ query = CGI.param('query');
+ page_count = POSIX.ceil(ctx.hit_count / ctx.page_size);
+%]
+
+<div id='form_div'>
+ <form action='./results' method='GET'>
+ <input type='text' name='query' size='50' value='[% query %]'/>
+ <input type='submit'/>
+ <input type='hidden' name='page' value='0'/>
+ </form>
+</div>
+
+<table id='body_table'>
+ <tr>
+ <td id='left_block'>
+ [% IF ctx.user; %]
+ <div id='links_div'>
+ <div><a href='home'>Home</a></div>
+ <div><a href='myopac'>Account</a></div>
+ <div><a href='logout'>Logout</a></div>
+ </div>
+ <hr/>
+ <table>
+ <tr><td colspan='2' style='border-bottom:1px solid #9A9'>Signed in as [% ctx.user.usrname %]</td></tr>
+ <tr><td>Total Holds</td><td>[% ctx.user_stats.holds.total %]</td></tr>
+ <tr><td>Ready Holds</td><td>[% ctx.user_stats.holds.ready %]</td></tr>
+ <tr><td>Items Out</td><td>[% ctx.user_stats.checkouts.out %]</td></tr>
+ <tr><td>Fines</td><td>$[% ctx.user_stats.fines.balance_owed %]</td></tr>
+ </table>
+ [% ELSE %]
+ [%
+ login = CGI.url("-path" => 1).replace('^http:', 'https:').replace('/results','/login');
+ origin = CGI.url("-absolute" => 1, "-path" => 1, "-query" => 1) | uri
+ %]
+ <a href='[% login _ '?origin=' _ origin %]'>Login</a>
+ [% END %]
+ </div>
+ <div>
+ [% FOR facet_type IN ctx.search_facets.keys %]
+ <b>[% facet_type %]</b>
+ <ul>
+ [% FOR facet IN ctx.search_facets.$facet_type.keys %]
+ <li>[% facet %] / [% ctx.search_facets.$facet_type.$facet %]</li>
+ [% END %]
+ </ul>
+ [% END %]
+ </div>
+ </td>
+ <td id='right_block'>
+ <div>
+ <span>Hits: [% ctx.hit_count %] / Page [% page + 1 %] of [% page_count %]</span>
+ <a [% IF page > 0 %] href='?page=[% page - 1 %]&query=[% query | uri %]' [% END %]>Prev</a>
+ <a [% IF (page + 1) < page_count %] href='?page=[% page + 1 %]&query=[% query | uri %]' [% END %]>Next</a>
+ </div>
+ <table id='record_table'>
+ [%
+ FOR rec IN ctx.records;
+ attrs = {marc_xml => rec.marc_xml};
+ PROCESS get_marc_attrs args=attrs;
+ %]
+ <tr>
+ <td style='width:52px;height:72px'>
+ [% IF attrs.isbn %]
+ <img width='50' height='70' src='/opac/extras/ac/jacket/small/[% attrs.isbn_clean || attrs.upc %]'/>
+ [% END %]
+ </td>
+ <td width='auto'>
+ <div>
+ <a href='rdetail?record=[% rec.bre.id %]'>[% attrs.title %]</a>
+ <span style='padding-left:10px;'>[% rec.copy_counts.available %] / [% rec.copy_counts.visible %]</span>
+ </div>
+ <div>[% attrs.author %]</div>
+ <div>[% attrs.isbn || attrs.issn || attrs.upc %] [% attrs.publisher %] [% attrs.pubdate %]</div>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+ </td>
+ </tr>
+
+</table>
+[% END %]
--- /dev/null
+<div style='position:absolute; top:0px; right:0px; border-left:1px solid #9A9; border-bottom:1px solid #9A9; padding: 10px; background:#ded'>
+ [% IF ctx.user; %]
+ <table>
+ <tr>
+ <td colspan='2' style='border-bottom:1px solid #9A9'>Welcome, [% ctx.user.usrname %]!</td>
+ </tr>
+ <tr>
+ <td>Total Holds</td><td>[% ctx.user_stats.holds.total %]</td>
+ </tr>
+ <tr>
+ <td>Ready Holds</td><td>[% ctx.user_stats.holds.ready %]</td>
+ </tr>
+ <tr>
+ <td>Items Out</td><td>[% ctx.user_stats.checkouts.out %]</td>
+ </tr>
+ <tr>
+ <td>Fines</td><td>$[% ctx.user_stats.fines.balance_owed %]</td>
+ </tr>
+ <tr>
+ <td><a href='logout'>Logout</a></td>
+ <td><a href='myopac'>Account</a></td>
+ </tr>
+ </table>
+ [% ELSE %]
+ <a href='[% ctx.base_url.replace('^http:', 'https:') _ ctx.base_path _ '/opac/login' %]'>Login</a>
+ [% END %]
+</div>