From 5a6956654d190aab736366bee72501eef06d09be Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 14 May 2012 15:22:08 -0400 Subject: [PATCH] TPAC added content integration https://bugs.launchpad.net/evergreen/+bug/984963 This adds a new tab on the detail page called Additional Content (suggestions welcome). When the tab is expanded, available content is presented to the user via a series of sub-tabs. At the start of loading the record detail page, kick off a series of asynchronous HTTP HEAD requests, one per type of added content. At the end of context loading, read the results of the HTTP requests for any that have completed. If the status for a type is 200, the type is marked as available. If it's not 200 (usually 404) it's marked as not available. Otherwise, it's marked as unknown. In the template, available content produces a link the user can click to view the content. Non-available content produces no links. Unknown content produces a link that may lead to content or no content when JS/dojo is disabled. When dojo is enabled, unknown content sends a series of JS queries to determine the state of the content asynchronously and results in displayed links for any type that is later determined to have content. TODO: when the user expands the Additional Content tab, no type is chosen by default. A default (available) type needs to be selected on initial display. TODO: better styling, particularly in the sub-tabs. Signed-off-by: Bill Erickson Signed-off-by: Mike Rylander --- .../perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm | 129 ++++++++++++++++++++- Open-ILS/src/templates/opac/parts/js.tt2 | 38 ++++++ .../templates/opac/parts/record/addedcontent.tt2 | 49 ++++++++ Open-ILS/src/templates/opac/parts/record/body.tt2 | 1 + .../src/templates/opac/parts/record/extras.tt2 | 3 +- Open-ILS/web/css/skin/default/opac/style.css | 4 + 6 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 Open-ILS/src/templates/opac/parts/record/addedcontent.tt2 diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm index af1c3fc3d4..f7d09f6eaa 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm @@ -5,8 +5,12 @@ use OpenSRF::Utils::Logger qw/$logger/; use OpenILS::Utils::CStoreEditor qw/:funcs/; use OpenILS::Utils::Fieldmapper; use OpenILS::Application::AppUtils; +use Net::HTTP::NB; +use IO::Select; my $U = 'OpenILS::Application::AppUtils'; +our $ac_types = ['toc', 'anotes', 'excerpt', 'summary', 'reviews']; + # context additions: # record : bre object sub load_record { @@ -15,6 +19,13 @@ sub load_record { $ctx->{page} = 'record'; $self->timelog("load_record() began"); + + my $rec_id = $ctx->{page_args}->[0] + or return Apache2::Const::HTTP_BAD_REQUEST; + + $self->added_content_stage1($rec_id); + $self->timelog("past added content stage 1"); + my $org = $self->_get_search_lib(); my $org_name = $ctx->{get_aou}->($org)->shortname; my $pref_ou = $self->_get_pref_lib(); @@ -29,9 +40,6 @@ sub load_record { my $copy_limit = int($self->cgi->param('copy_limit') || 10); my $copy_offset = int($self->cgi->param('copy_offset') || 0); - my $rec_id = $ctx->{page_args}->[0] - or return Apache2::Const::HTTP_BAD_REQUEST; - $self->get_staff_search_settings; if ($ctx->{staff_saved_search_size}) { $ctx->{saved_searches} = ($self->staff_load_searches)[1]; @@ -126,6 +134,11 @@ sub load_record { } $self->timelog("past expandies"); + + $self->added_content_stage2($rec_id); + + $self->timelog("past added content stage 2"); + return Apache2::Const::OK; } @@ -398,4 +411,114 @@ sub load_email_record { return Apache2::Const::OK; } +# for each type, fire off the reqeust to see if content is available +# ctx.added_content.$type.status: +# 1 == available +# 2 == not available +# 3 == unknown +sub added_content_stage1 { + my $self = shift; + my $rec_id = shift; + my $ctx = $self->ctx; + my $sel_type = $self->cgi->param('ac') || ''; + my $key = $self->get_ac_key($rec_id); + ($key = $key->{value}) =~ s/^\s+//g if $key; + + $ctx->{added_content} = {}; + for my $type (@$ac_types) { + $ctx->{added_content}->{$type} = {content => ''}; + $ctx->{added_content}->{$type}->{status} = $key ? 3 : 2; + + if ($key) { + $logger->debug("tpac: starting added content request for $key => $type"); + + my $req = Net::HTTP::NB->new(Host => $self->apache->hostname); + + if (!$req) { + $logger->warn("Unable to fetch added content from " . $self->apache->hostname . ": $@"); + next; + } + + my $http_type = ($type eq $sel_type) ? 'GET' : 'HEAD'; + $req->write_request($http_type => "/opac/extras/ac/$type/html/$key"); + $ctx->{added_content}->{$type}->{request} = $req; + } + } +} + +# check each outstanding request. If it's ready, read the HTTP +# status and use it to determine if content is available. Otherwise, +# leave the status as unknown. +sub added_content_stage2 { + my $self = shift; + my $ctx = $self->ctx; + my $sel_type = $self->cgi->param('ac') || ''; + + for my $type (keys %{$ctx->{added_content}}) { + my $content = $ctx->{added_content}->{$type}; + + if ($content->{status} == 3) { + $logger->debug("tpac: finishing added content request for $type"); + + my $req = $content->{request}; + my $sel = IO::Select->new($req); + + # if we are requesting a specific type of content, give the + # backend code a little extra time to retrieve the content. + my $wait = $type eq $sel_type ? 3 : 0; # TODO: config? + + if ($sel->can_read($wait)) { + my ($code) = $req->read_response_headers; + $content->{status} = $code eq '200' ? 1 : 2; + $logger->debug("tpac: added content request for $type returned $code"); + + if ($type eq $sel_type) { + while (1) { + my $buf; + my $n = $req->read_entity_body($buf, 1024); + last unless $n; + $content->{content} .= $buf; + } + } + } + } + } +} + +# XXX this is copied directly from AddedContent.pm in +# working/user/jeff/ac_by_record_id_rebase. When Jeff's +# branch is merged and Evergreen gets added content +# lookup by ID, this can be removed. +# returns [{tag => $tag, value => $value}, {tag => $tag2, value => $value2}] +sub get_ac_key { + my $self = shift; + my $rec_id = shift; + my $key_data = $self->editor->json_query({ + select => {mfr => ['tag', 'value']}, + from => 'mfr', + where => { + record => $rec_id, + '-or' => [ + { + '-and' => [ + {tag => '020'}, + {subfield => 'a'} + ] + }, { + '-and' => [ + {tag => '024'}, + {subfield => 'a'}, + {ind1 => 1} + ] + } + ] + } + }); + + return ( + grep {$_->{tag} eq '020'} @$key_data, + grep {$_->{tag} eq '024'} @$key_data + )[0]; +} + 1; diff --git a/Open-ILS/src/templates/opac/parts/js.tt2 b/Open-ILS/src/templates/opac/parts/js.tt2 index df50a03559..b29a0bcdc0 100644 --- a/Open-ILS/src/templates/opac/parts/js.tt2 +++ b/Open-ILS/src/templates/opac/parts/js.tt2 @@ -77,4 +77,42 @@ [% END; # use_autosuggest %] + + [%- END; # want_dojo -%] diff --git a/Open-ILS/src/templates/opac/parts/record/addedcontent.tt2 b/Open-ILS/src/templates/opac/parts/record/addedcontent.tt2 new file mode 100644 index 0000000000..21e81ef29f --- /dev/null +++ b/Open-ILS/src/templates/opac/parts/record/addedcontent.tt2 @@ -0,0 +1,49 @@ +
+ +[% + ac_types = { + reviews => l('Reviews'), + anotes => l('Author Notes'), + toc => l('Table of Contents'), + excerpt => l('Excerpt'), + summary => l('Summary') + }; + + selected_type = CGI.param('ac'); + + # For each type of added content, render the link if it's known to have + # content, do not render the link if it's known to not have content. If + # the content status is unknown, render the link, but hide the link via CSS + # if dojo is enabled. If dojo is not enabled, render and display the link. +%] + +
+ [% FOR type IN ac_types.keys; + tab_class = 'ac_tab'; + IF ctx.added_content.$type.status != '2'; # no content + IF ctx.added_content.$type.status == '3' AND want_dojo; # status unknown + tab_class = tab_class _ ' hidden'; + END %] + + [% END; + END %] +
+ +
+
+ [% + IF selected_type; + content = ctx.added_content.$selected_type.content; + IF content; + content; + ELSE; + l('No Content Available'); + END; + END; + %] +
+
+ + diff --git a/Open-ILS/src/templates/opac/parts/record/body.tt2 b/Open-ILS/src/templates/opac/parts/record/body.tt2 index b7efd51790..664abc9dbf 100644 --- a/Open-ILS/src/templates/opac/parts/record/body.tt2 +++ b/Open-ILS/src/templates/opac/parts/record/body.tt2 @@ -2,6 +2,7 @@ PROCESS "opac/parts/misc_util.tt2"; PROCESS get_marc_attrs args=attrs; stop_parms = ['expand','cnoffset']; + ctx.record_attrs = attrs; # capture for JS %]
diff --git a/Open-ILS/src/templates/opac/parts/record/extras.tt2 b/Open-ILS/src/templates/opac/parts/record/extras.tt2 index 4a473c3660..63f82eeb6a 100644 --- a/Open-ILS/src/templates/opac/parts/record/extras.tt2 +++ b/Open-ILS/src/templates/opac/parts/record/extras.tt2 @@ -22,6 +22,7 @@ {name => 'excerpt', label => l('Excerpt'), hide => 1}, {name => 'issues', label => l('Issues Held'), hide => !(ctx.have_holdings_to_show || ctx.have_mfhd_to_show)}, {name => 'preview', label => l('Preview'), hide => 1}, + {name => 'addedcontent', label => l('Additional Content')}, # hide if all content is known to not exist {name => 'cnbrowse', label => l('Shelf Browser')}, {name => 'marchtml', label => l('MARC Record')} ]; @@ -40,7 +41,7 @@