TPac: Page through search results on details page
authorBill Erickson <berick@esilibrary.com>
Wed, 14 Sep 2011 18:25:41 +0000 (14:25 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Mon, 26 Sep 2011 16:10:06 +0000 (12:10 -0400)
Consistent with jspac, when you land on a record details page and you
got there from doing a search, let the user page through the search
results directly on the record detail page.

This also includes the necessary JS callbacks to support paging in the
staff client.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm
Open-ILS/src/templates/opac/parts/js.tt2
Open-ILS/src/templates/opac/parts/record/body.tt2
Open-ILS/src/templates/opac/parts/searchbar.tt2
Open-ILS/web/css/skin/default/opac/style.css
Open-ILS/web/js/ui/default/opac/staff.js

index 009372c..9b21d56 100644 (file)
@@ -12,7 +12,7 @@ my $U = 'OpenILS::Application::AppUtils';
 sub load_record {
     my $self = shift;
     my $ctx = $self->ctx;
-    $ctx->{page} = 'record';
+    $ctx->{page} = 'record';  
 
     my $org = $self->cgi->param('loc') || $ctx->{aou_tree}->()->id;
     my $depth = $self->cgi->param('depth') || 0;
@@ -27,6 +27,8 @@ sub load_record {
         $ctx->{saved_searches} = ($self->staff_load_searches)[1];
     }
 
+    $self->fetch_related_search_info($rec_id);
+
     # run copy retrieval in parallel to bib retrieval
     # XXX unapi
     my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
@@ -99,6 +101,33 @@ sub load_record {
     return Apache2::Const::OK;
 }
 
+# collect IDs and info on the search that lead to this details page
+# If no search query, etc is present, we leave ctx.search_result_index == -1
+sub fetch_related_search_info {
+    my $self = shift;
+    my $rec_id = shift;
+    my $ctx = $self->ctx;
+    $ctx->{search_result_index} = -1;
+
+    $self->load_rresults(internal => 1);
+
+    my @search_ids = @{$ctx->{ids}};
+    return unless @search_ids;
+
+    for my $idx (0..$#search_ids) {
+        if ($search_ids[$idx] == $rec_id) {
+            $ctx->{prev_search_record} = $search_ids[$idx - 1] if $idx > 0;
+            $ctx->{next_search_record} = $search_ids[$idx + 1];
+            $ctx->{search_result_index} = $idx;
+            last;
+        }
+    }
+
+    $ctx->{first_search_record} = $search_ids[0];
+    $ctx->{last_search_record} = $search_ids[-1];
+}
+
+
 sub mk_copy_query {
     my $self = shift;
     my $rec_id = shift;
index 7dc650b..635678b 100644 (file)
@@ -10,6 +10,10 @@ use Data::Dumper;
 $Data::Dumper::Indent = 0;
 my $U = 'OpenILS::Application::AppUtils';
 
+# when fetching "all" search results for staff client 
+# start/end paging, fetch this many IDs at most
+my $all_recs_limit = 10000;
+
 
 sub _prepare_biblio_search_basics {
     my ($cgi) = @_;
@@ -140,15 +144,21 @@ sub _get_search_limit {
 #   records : list of bre's and copy-count objects
 sub load_rresults {
     my $self = shift;
+    my %args = @_;
+    my $internal = $args{internal};
     my $cgi = $self->cgi;
     my $ctx = $self->ctx;
     my $e = $self->editor;
 
-    $ctx->{page} = 'rresult';
+    $ctx->{page} = 'rresult' unless $internal;
+    $ctx->{ids} = [];
+    $ctx->{records} = [];
+    $ctx->{search_facets} = {};
+    $ctx->{hit_count} = 0;
 
     # Special alternative searches here.  This could all stand to be cleaner.
     if ($cgi->param("_special")) {
-        return $self->marc_expert_search if scalar($cgi->param("tag"));
+        return $self->marc_expert_search(%args) if scalar($cgi->param("tag"));
         return $self->item_barcode_shortcut if (
             $cgi->param("qtype") and ($cgi->param("qtype") eq "item_barcode")
         );
@@ -165,6 +175,15 @@ sub load_rresults {
     my $metarecord = $cgi->param('metarecord');
     my $results; 
 
+    $ctx->{page_size} = $limit;
+    $ctx->{search_page} = $page;
+
+    # fetch the first hit from the next page
+    if ($internal) {
+        $limit = $all_recs_limit;
+        $offset = 0;
+    }
+
     my ($query, $site, $depth) = _prepare_biblio_search($cgi, $ctx);
 
     $self->get_staff_search_settings;
@@ -184,7 +203,7 @@ sub load_rresults {
         }
     }
 
-    if ($metarecord) {
+    if ($metarecord and !$internal) {
 
         # TODO: other limits, like SVF/format, etc.
         $results = $U->simplereq(
@@ -198,7 +217,10 @@ sub load_rresults {
 
     } else {
 
-        return $self->generic_redirect unless $query;
+        if (!$query) {
+            return Apache2::Const::OK if $internal;
+            return $self->generic_redirect;
+        }
 
         # Limit and offset will stay here. Everything else should be part of
         # the query string, not special args.
@@ -226,13 +248,11 @@ sub load_rresults {
 
     my $rec_ids = [map { $_->[0] } @{$results->{ids}}];
 
-    $ctx->{records} = [];
-    $ctx->{search_facets} = {};
-    $ctx->{page_size} = $limit;
+    $ctx->{ids} = $rec_ids;
     $ctx->{hit_count} = $results->{count};
     $ctx->{parsed_query} = $results->{parsed_query};
 
-    return Apache2::Const::OK if @$rec_ids == 0;
+    return Apache2::Const::OK if @$rec_ids == 0 or $internal;
 
     my ($facets, @data) = $self->get_records_and_facets(
         $rec_ids, $results->{facet_key}, 
@@ -306,7 +326,7 @@ sub item_barcode_shortcut {
 # like item_barcode_search, this can't take all the usual search params, but
 # this one will at least do site, limit and page
 sub marc_expert_search {
-    my ($self) = @_;
+    my ($self, %args) = @_;
 
     my @tags = $self->cgi->param("tag");
     my @subfields = $self->cgi->param("subfield");
@@ -334,10 +354,17 @@ sub marc_expert_search {
     $self->ctx->{search_facets} = {};
     $self->ctx->{page_size} = $limit;
     $self->ctx->{hit_count} = 0;
+    $self->ctx->{ids} = [];
+    $self->ctx->{search_page} = $page;
         
     # nothing to do
     return Apache2::Const::OK if @$query == 0;
 
+    if ($args{internal}) {
+        $limit = $all_recs_limit;
+        $offset = 0;
+    }
+
     my $timeout = 120;
     my $ses = OpenSRF::AppSession->create('open-ils.search');
     my $req = $ses->request(
@@ -357,14 +384,13 @@ sub marc_expert_search {
         return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    $self->ctx->{ids} = [ grep { $_ } @{$results->{ids}} ];
+
     my ($facets, @data) = $self->get_records_and_facets(
-        # filter out nulls that will turn up here
-        [ grep { $_ } @{$results->{ids}} ],
-        undef, {flesh => "{holdings_xml,mra}"}
+        $self->ctx->{ids}, undef, {flesh => "{holdings_xml,mra}"}
     );
 
     $self->ctx->{records} = [@data];
-    $self->ctx->{page_size} = $limit;
     $self->ctx->{hit_count} = $results->{count};
 
     return Apache2::Const::OK;
index 22ae555..c0b2e70 100644 (file)
@@ -5,6 +5,18 @@
 
 [%- IF ctx.is_staff %]
 <script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/staff.js"></script>
+    [% IF ctx.page == 'record' AND ctx.search_result_index >= 0 %]
+    <script>
+        rdetail_next_prev_actions(
+            "[% ctx.search_result_index %]",
+            "[% ctx.hit_count %]",
+            "[% ctx.prev_rec_url || '' %]",
+            "[% ctx.next_rec_url || '' %]",
+            "[% mkurl(ctx.first_search_record, {page => 0}) %]",
+            "[% mkurl(ctx.last_search_record, {page => POSIX.floor(ctx.hit_count / ctx.page_size)}) %]"
+        );
+    </script>
+    [% END %]
 [%- END %]
 
 [%- IF ENV.OILS_NOVELIST_URL AND ctx.page == 'record';
index 184bd7c..bc42f5d 100644 (file)
@@ -2,40 +2,48 @@
 [%  attrs = {marc_xml => ctx.marc_xml};
     PROCESS "opac/parts/misc_util.tt2";
     PROCESS get_marc_attrs args=attrs %]
+
 <div id='canvas_main' class='canvas'>
-    <div id="rdetail_header" class="hide_me">[%#
-        XXX Does it make sense for now to even have this section?  Why
-        should the record detail page be aware of the ongoing search?
-        The user can use the back button to go back to their search results
-        like on any other website. %]
+
+    [% IF ctx.search_result_index >= 0 %]
+    <div id="rdetail_header">
         <div style="float:left;">
-            Search Results&nbsp;&nbsp;&nbsp;
-            <span id="rdetail_result_count" class="hide_me">
-                Showing Item <strong id='np_offset'></strong>
-                &nbsp;of&nbsp;<strong id='np_count'></strong>
+            <a href='[% mkurl(ctx.opac_root _ '/results', {page => 0}) %]'>[% l('&#9668; Search Results') %]</a>
+            <span id="rdetail_result_count">
+                [% l('Showing Item [_1] of [_2]', ctx.search_result_index + 1, ctx.hit_count) %]
             </span>
         </div>
         <div id="rdetail_result_nav">
-            <span class="hide_me">
-                <a class='np_nav_link classic_link' id='np_start'
-                    href='#'
-                    title="[% l("First results page") %]">[% l("Start") %]</a>
-                </span>
-                <a class='np_nav_link classic_link hide_me' id='np_prev'
-                    href='#'
-                    title='[% l("Previous page") %]'><span
-                        class="nav_arrow_fix">&#9668;</span> Previous</a>
-                <span style="padding:0px 10px;"> </span>
-                <a class='np_nav_link classic_link hide_me' id='np_next'
-                    href='#'
-                    title='[% l("Next page") %]'>Next <span
-                        class="nav_arrow_fix">&#9658;</span></a>
-                <span class="hide_me"><a class='np_nav_link classic_link'
-                    id='np_end' href='#'
-                    title="[% l("Last results page") %]">[% l("End") %]</a></span>
+            [%
+                IF ctx.prev_search_record;
+                    prev_args = {};
+                    IF ctx.search_result_index % (ctx.page_size + 1) == 0; # first record in the page
+                        prev_args.page = ctx.search_page - 1;
+                    END;
+                    ctx.prev_rec_url = mkurl(ctx.prev_search_record, prev_args);
+            %]
+            <a class='np_nav_link classic_link' title='[% l("Previous Record") %]'
+                href='[% ctx.prev_rec_url %]'><span class="nav_arrow_fix">&#9668; </span>[% l('Previous') %]</a>
+            [% END %]
+
+            <span style="padding:0px 10px;"> </span>
+
+            [% 
+                IF ctx.next_search_record;
+                    next_args = {};
+                    IF ctx.page_size == ctx.search_result_index + 1;
+                        next_args.page = ctx.search_page + 1;
+                    END;
+                    ctx.next_rec_url = mkurl(ctx.next_search_record, next_args);
+            %]
+            <a class='np_nav_link classic_link' title='[% l("Next Record") %]'
+                href='[% ctx.next_rec_url %]'>[% l('Next') %]<span class="nav_arrow_fix"> &#9658;</span></a>
+            [% END %]
+
         </div>
         <div class="clear-both"></div>
     </div>
+    [% END %]
 
     <div style='font-weight: bold; padding: 5px; margin: 5px; width: 100%;'
         class='hide_me color_4' id='rdetail_deleted_exp'>
index 3dbd70a..1f1f0a8 100644 (file)
         %]</a> ]
     </div>
     [% END %]
+    <!--
     <div id="breadcrumb">
         <a href="[% ctx.opac_root %]/home">[% l('Catalog Home') %]</a> &gt;
     </div>
+    -->
     <div class="clear-both"></div>
 </div>
index 7c1cd69..cc8dcf4 100644 (file)
@@ -342,6 +342,7 @@ div.select-wrapper:hover {
        color: black;
        font-size: 11px;
        font-weight: normal;
+    padding-left: 10px;
 }
 
 #rdetail_result_nav {
index c170198..5521e50 100644 (file)
@@ -24,3 +24,29 @@ window.onload = function() {
     if(rec && rec[1]) { runEvt('rdetail', 'recordRetrieved', rec[1]); }
     // fire other events the staff client is expecting...
 }
+
+function rdetail_next_prev_actions(index, count, prev, next, start, end) {
+    /*  we get the relative URL from the template:  recid?query_args...
+        replace the recid and args on location.href to get the new URL  */
+    function fullurl(url) { return location.href.replace(/\/\d+\??.*/, '/' + url); }
+
+    if (index > 0) {
+        if(prev) 
+            window.rdetailPrev = function() { location.href = fullurl(prev); }
+        if(start) 
+            window.rdetailStart = function() { location.href = fullurl(start); }
+    }
+
+    if (index < count - 1) {
+        if(next) 
+            window.rdetailNext = function() { location.href = fullurl(next); }
+        if(end) 
+            window.rdetailEnd = function() { location.href = fullurl(end); }
+    }
+
+    ol = window.onload;
+    window.onload = function() {
+        if(ol) ol(); 
+        runEvt('rdetail', 'nextPrevDrawn', Number(index), Number(count)); 
+    };
+}