From: Bill Erickson Date: Tue, 18 Jan 2011 14:59:16 +0000 (-0500) Subject: Proof of concept Template-Toolkit OPAC X-Git-Tag: opac-tt-poc-demo2~118 X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=93cc378ec835e778dbc90fa04f150368e38e5afd;p=evergreen%2Fequinox.git Proof of concept Template-Toolkit OPAC --- diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/OpenILS/WWW/EGCatLoader.pm new file mode 100644 index 0000000000..18afc95cb9 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/WWW/EGCatLoader.pm @@ -0,0 +1,405 @@ +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; diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm b/Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm index 42eb6ffbf7..50de099ac7 100644 --- a/Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm +++ b/Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm @@ -7,6 +7,7 @@ use File::stat; 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'; @@ -32,16 +33,21 @@ sub handler { 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})) { @@ -52,6 +58,30 @@ sub handler { 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; @@ -65,7 +95,7 @@ sub parse_as_xml { $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'); @@ -79,7 +109,8 @@ sub parse_as_xml { 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'; @@ -192,6 +223,7 @@ sub parse_config { $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}; @@ -217,7 +249,7 @@ sub parse_config { } } - return {ctx => $ctx, handlers => $handlers}; + return {base_ctx => $ctx, handlers => $handlers}; } package PathConfig; diff --git a/Open-ILS/web/templates/default/opac/base.tt2 b/Open-ILS/web/templates/default/opac/base.tt2 new file mode 100644 index 0000000000..6d546077d5 --- /dev/null +++ b/Open-ILS/web/templates/default/opac/base.tt2 @@ -0,0 +1,12 @@ + + + + [% ctx.page_title %] + + [% BLOCK html_head; END; # provide a default that can be overridden %] + [% PROCESS html_head %] + + + [% content %] + + diff --git a/Open-ILS/web/templates/default/opac/common.tt2 b/Open-ILS/web/templates/default/opac/common.tt2 new file mode 100644 index 0000000000..0500725cd6 --- /dev/null +++ b/Open-ILS/web/templates/default/opac/common.tt2 @@ -0,0 +1,27 @@ +[% + # 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; +%] + + [% END %] +[% END %] + +[% PROCESS 'default/opac/marc_attrs.tt2' %] + + diff --git a/Open-ILS/web/templates/default/opac/home.tt2 b/Open-ILS/web/templates/default/opac/home.tt2 new file mode 100644 index 0000000000..d1c2e05d1b --- /dev/null +++ b/Open-ILS/web/templates/default/opac/home.tt2 @@ -0,0 +1,22 @@ +[% ctx.page_title = "Home" %] + +[% BLOCK html_head %] + +[% END %] + +[% WRAPPER "default/opac/base.tt2" %] + +
+ +

+
+ + + +
+
+ + +[% END %] diff --git a/Open-ILS/web/templates/default/opac/login.tt2 b/Open-ILS/web/templates/default/opac/login.tt2 new file mode 100644 index 0000000000..a6e8b141df --- /dev/null +++ b/Open-ILS/web/templates/default/opac/login.tt2 @@ -0,0 +1,32 @@ +[% BLOCK html_head %] + +[% END %] + +[% + USE CGI; + WRAPPER "default/opac/base.tt2"; + ctx.page_title = "Login"; +%] + +
+
+ + + + + + + + + + + + +
Username or Barcode
Password
+ +
+ +
+
+[% END %] diff --git a/Open-ILS/web/templates/default/opac/marc_attrs.tt2 b/Open-ILS/web/templates/default/opac/marc_attrs.tt2 new file mode 100644 index 0000000000..9cdad34c5d --- /dev/null +++ b/Open-ILS/web/templates/default/opac/marc_attrs.tt2 @@ -0,0 +1,17 @@ +[% + # 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; +%] diff --git a/Open-ILS/web/templates/default/opac/myopac.tt2 b/Open-ILS/web/templates/default/opac/myopac.tt2 new file mode 100644 index 0000000000..fc8c74cff0 --- /dev/null +++ b/Open-ILS/web/templates/default/opac/myopac.tt2 @@ -0,0 +1,44 @@ +[% BLOCK html_head %] + +[% END %] + +[% + WRAPPER "default/opac/base.tt2"; + ctx.page_title = "My Account"; +%] + + + + + + + + + + + + + + + + + + + + + + + + + + +
First Name[% ctx.user.first_given_name %]
Middle Name[% ctx.user.second_given_name %]
Last Name[% ctx.user.family_name %]
Library Card[% ctx.user.card.barcode %]
Email Address[% ctx.user.email %]
Phone[% ctx.user.day_phone %]
+ + + +[% END %] diff --git a/Open-ILS/web/templates/default/opac/place_hold.tt2 b/Open-ILS/web/templates/default/opac/place_hold.tt2 new file mode 100644 index 0000000000..b92315bd75 --- /dev/null +++ b/Open-ILS/web/templates/default/opac/place_hold.tt2 @@ -0,0 +1,32 @@ +[% BLOCK html_head %] + +[% 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; +%] + + +
+
Placing hold on [% attrs.title %], by [% attrs.author %]
+ [% IF ctx.hold_success %] +
Succeeded
+ [% ELSIF ctx.hold_failed %] +
Failed...
+ [% ELSE %] +
+ Choose a pickup Library [% PROCESS build_org_selector name='pickup_lib' value=ctx.default_pickup_lib %] + + + +
+ [% END %] +
+ +[% END %] diff --git a/Open-ILS/web/templates/default/opac/rdetail.tt2 b/Open-ILS/web/templates/default/opac/rdetail.tt2 new file mode 100644 index 0000000000..a4966fd24b --- /dev/null +++ b/Open-ILS/web/templates/default/opac/rdetail.tt2 @@ -0,0 +1,80 @@ +[% BLOCK html_head %] + +[% 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; +%] + +
+ + + + + [% IF attrs.title %][% END %] + [% IF attrs.author %][% END %] + [% IF attrs.isbn %][% END %] + [% IF attrs.issn %][% END %] + [% IF attrs.upc %][% END %] + [% IF attrs.pubdate %][% END %] + [% IF attrs.publisher %][% END %] + + + + +
+ +
Title[% attrs.title %]
Author[% attrs.author %]
ISBN[% attrs.isbn %]
ISSN[% attrs.issn %]
UPC[% attrs.upc %]
Publication Date[% attrs.pubdate %]
Publishere[% attrs.publisher %]
Subjects + [% FOR node IN marc_xml.findnodes('//*[@tag="650"]') %] + [% + s0 = node.childNodes.0.textContent; + s1 = node.childNodes.1.textContent; + %] + [% IF s0 %] + [% s0 %] + [% IF s1 %] + -- + [% s1 %] + [% END %] +
+ [% END %] + [% END %] +
+ + + + + + + + + + + [% FOR acn IN record.call_numbers %] + [% FOR acp IN acn.copies %] + + + + + + + [% END %] + [% END %] + +
Owning LibCall NumberBarcodeStatus
[% ctx.find_org_unit(acn.owning_lib).shortname %][% acn.label %][% acp.barcode %][% acp.status %]
+
+ + +[% END %] diff --git a/Open-ILS/web/templates/default/opac/records.tt2 b/Open-ILS/web/templates/default/opac/records.tt2 new file mode 100644 index 0000000000..7d53268286 --- /dev/null +++ b/Open-ILS/web/templates/default/opac/records.tt2 @@ -0,0 +1,31 @@ +[% WRAPPER "default/opac/base.tt2" %] +[% ctx.page_title = "Results" %] +[% page = ctx.cgi.param('page') || 0; %] +Prev Next +
+ + +[% + 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; +%] + + + + + + + [% END %] +
[% FOR attr IN attrs; attr.attr_value _ ' / '; END; %]
+[% END %] diff --git a/Open-ILS/web/templates/default/opac/results.tt2 b/Open-ILS/web/templates/default/opac/results.tt2 new file mode 100644 index 0000000000..8704eeb6d3 --- /dev/null +++ b/Open-ILS/web/templates/default/opac/results.tt2 @@ -0,0 +1,102 @@ +[% BLOCK html_head %] + +[% 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); +%] + +
+
+ + + +
+
+ + + + + + + +
+ [% IF ctx.user; %] + +
+ + + + + + +
Signed in as [% ctx.user.usrname %]
Total Holds[% ctx.user_stats.holds.total %]
Ready Holds[% ctx.user_stats.holds.ready %]
Items Out[% ctx.user_stats.checkouts.out %]
Fines$[% ctx.user_stats.fines.balance_owed %]
+ [% ELSE %] + [% + login = CGI.url("-path" => 1).replace('^http:', 'https:').replace('/results','/login'); + origin = CGI.url("-absolute" => 1, "-path" => 1, "-query" => 1) | uri + %] + Login + [% END %] + +
+ [% FOR facet_type IN ctx.search_facets.keys %] + [% facet_type %] +
    + [% FOR facet IN ctx.search_facets.$facet_type.keys %] +
  • [% facet %] / [% ctx.search_facets.$facet_type.$facet %]
  • + [% END %] +
+ [% END %] +
+
+
+ Hits: [% ctx.hit_count %] / Page [% page + 1 %] of [% page_count %] + 0 %] href='?page=[% page - 1 %]&query=[% query | uri %]' [% END %]>Prev + Next +
+ + [% + FOR rec IN ctx.records; + attrs = {marc_xml => rec.marc_xml}; + PROCESS get_marc_attrs args=attrs; + %] + + + + + [% END %] +
+ [% IF attrs.isbn %] + + [% END %] + +
+ [% attrs.title %] + [% rec.copy_counts.available %] / [% rec.copy_counts.visible %] +
+
[% attrs.author %]
+
[% attrs.isbn || attrs.issn || attrs.upc %] [% attrs.publisher %] [% attrs.pubdate %]
+
+
+[% END %] diff --git a/Open-ILS/web/templates/default/opac/welcome.tt2 b/Open-ILS/web/templates/default/opac/welcome.tt2 new file mode 100644 index 0000000000..85d19ac253 --- /dev/null +++ b/Open-ILS/web/templates/default/opac/welcome.tt2 @@ -0,0 +1,27 @@ +
+ [% IF ctx.user; %] + + + + + + + + + + + + + + + + + + + + +
Welcome, [% ctx.user.usrname %]!
Total Holds[% ctx.user_stats.holds.total %]
Ready Holds[% ctx.user_stats.holds.ready %]
Items Out[% ctx.user_stats.checkouts.out %]
Fines$[% ctx.user_stats.fines.balance_owed %]
LogoutAccount
+ [% ELSE %] + Login + [% END %] +