Bug 10856: Improve the previous and next items on the shelf browser
authorJonathan Druart <jonathan.druart@biblibre.com>
Tue, 14 May 2013 13:11:11 +0000 (15:11 +0200)
committerGalen Charlton <gmc@esilibrary.com>
Fri, 4 Oct 2013 15:56:35 +0000 (15:56 +0000)
The next and previous links should completely refresh the shelf.

For example:
[<] [1] [2] [3] [4] [5] [6] [>]
Before this patch, the next and previous links were the same as the 1
and 6.
With this patch, after clicking on next, we will get:
[<] [7] [8] [9] [10] [11] [12] [13] [>]

This patch adds a new AJAX script to get the shelf browser block.

Test plan:
- On a detail biblio page, click on a "Browse shelf" link.
- Play with the next and previous links.
- Deactivate Javascript (using NoScript for example) and check that you
  get the same behavior (but the page is reloaded).
- Launch the unit tests: prove t/db_dependent/ShelfBrowser.t

Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de>
Passes all tests and QA script.

Signed-off-by: Galen Charlton <gmc@esilibrary.com>
C4/ShelfBrowser.pm
koha-tmpl/opac-tmpl/prog/en/includes/shelfbrowser.inc [new file with mode: 0644]
koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
koha-tmpl/opac-tmpl/prog/en/modules/svc/shelfbrowser.tt [new file with mode: 0644]
opac/opac-detail.pl
opac/svc/shelfbrowser.pl [new file with mode: 0755]
t/db_dependent/ShelfBrowser.t [new file with mode: 0644]

index c885d90..555ad05 100644 (file)
@@ -63,10 +63,9 @@ to take into account.
 
   $nearby = GetNearbyItems($itemnumber, [$num_each_side]);
 
 
   $nearby = GetNearbyItems($itemnumber, [$num_each_side]);
 
-  @next = @{ $nearby->{next} };
-  @prev = @{ $nearby->{prev} };
+  @items = @{ $nearby->{items} };
 
 
-  foreach (@next) {
+  foreach (@items) {
       # These won't format well like this, but here are the fields
          print $_->{title};
          print $_->{biblionumber};
       # These won't format well like this, but here are the fields
          print $_->{title};
          print $_->{biblionumber};
@@ -78,10 +77,10 @@ to take into account.
 
   # This is the information required to scroll the browser to the next left
   # or right set. Can be derived from next/prev, but it's here for convenience.
 
   # This is the information required to scroll the browser to the next left
   # or right set. Can be derived from next/prev, but it's here for convenience.
-  print $nearby->{prev_itemnumber};
-  print $nearby->{next_itemnumber};
-  print $nearby->{prev_biblionumber};
-  print $nearby->{next_biblionumber};
+  print $nearby->{prev_item}{itemnumber};
+  print $nearby->{next_item}{itemnumber};
+  print $nearby->{prev_item}{biblionumber};
+  print $nearby->{next_item}{biblionumber};
 
   # These will be undef if the values are not used to calculate the 
   # nearby items.
 
   # These will be undef if the values are not used to calculate the 
   # nearby items.
@@ -92,8 +91,6 @@ to take into account.
   print $nearby->{starting_ccode}->{code};
   print $nearby->{starting_ccode}->{description};
 
   print $nearby->{starting_ccode}->{code};
   print $nearby->{starting_ccode}->{description};
 
-  print $nearby->{starting_itemnumber};
-  
 This finds the items that are nearby to the supplied item, and supplies
 those previous and next, along with the other useful information for displaying
 the shelf browser.
 This finds the items that are nearby to the supplied item, and supplies
 those previous and next, along with the other useful information for displaying
 the shelf browser.
@@ -113,11 +110,13 @@ This will throw an exception if something went wrong.
 =cut
 
 sub GetNearbyItems {
 =cut
 
 sub GetNearbyItems {
-       my ($itemnumber, $num_each_side) = @_;
-       $num_each_side ||= 3;
+    my ( $itemnumber, $num_each_side, $gap) = @_;
+    $num_each_side ||= 3;
+    $gap ||= 7; # Should be > $num_each_side
+    die "BAD CALL in C4::ShelfBrowser::GetNearbyItems, gap should be > num_each_side"
+        if $gap <= $num_each_side;
 
     my $dbh         = C4::Context->dbh;
 
     my $dbh         = C4::Context->dbh;
-    my $marcflavour = C4::Context->preference("marcflavour");
     my $branches = GetBranches();
 
     my $sth_get_item_details = $dbh->prepare("SELECT cn_sort,homebranch,location,ccode from items where itemnumber=?");
     my $branches = GetBranches();
 
     my $sth_get_item_details = $dbh->prepare("SELECT cn_sort,homebranch,location,ccode from items where itemnumber=?");
@@ -145,12 +144,12 @@ sub GetNearbyItems {
 
     # Build the query for previous and next items
     my $prev_query ='
 
     # Build the query for previous and next items
     my $prev_query ='
-        SELECT *
+        SELECT itemnumber, biblionumber, cn_sort, itemcallnumber
         FROM items
         WHERE
             ((cn_sort = ? AND itemnumber < ?) OR cn_sort < ?) ';
     my $next_query ='
         FROM items
         WHERE
             ((cn_sort = ? AND itemnumber < ?) OR cn_sort < ?) ';
     my $next_query ='
-        SELECT *
+        SELECT itemnumber, biblionumber, cn_sort, itemcallnumber
         FROM items
         WHERE
             ((cn_sort = ? AND itemnumber >= ?) OR cn_sort > ?) ';
         FROM items
         WHERE
             ((cn_sort = ? AND itemnumber >= ?) OR cn_sort > ?) ';
@@ -170,60 +169,68 @@ sub GetNearbyItems {
        push @params, $start_ccode->{code};
     }
 
        push @params, $start_ccode->{code};
     }
 
-    my $sth_prev_items = $dbh->prepare($prev_query . $query_cond . ' ORDER BY cn_sort DESC, itemnumber LIMIT ?');
-    my $sth_next_items = $dbh->prepare($next_query . $query_cond . ' ORDER BY cn_sort, itemnumber LIMIT ?');
-    push @params, $num_each_side;
-    $sth_prev_items->execute(@params);
-    $sth_next_items->execute(@params);
-    
-    # Now we have the query run, suck out the data like marrow
-    my @prev_items = reverse GetShelfInfo($sth_prev_items, $marcflavour);
-    my @next_items = GetShelfInfo($sth_next_items, $marcflavour);
-
-    my (
-        $next_itemnumber, $next_biblionumber,
-        $prev_itemnumber, $prev_biblionumber
-    );
-
-    $next_itemnumber = $next_items[-1]->{itemnumber} if @next_items;
-    $next_biblionumber = $next_items[-1]->{biblionumber} if @next_items;
-
-    $prev_itemnumber = $prev_items[0]->{itemnumber} if @prev_items;
-    $prev_biblionumber = $prev_items[0]->{biblionumber} if @prev_items;
-
-    my %result = (
-        next                => \@next_items,
-        prev                => \@prev_items,
-        next_itemnumber     => $next_itemnumber,
-        next_biblionumber   => $next_biblionumber,
-        prev_itemnumber     => $prev_itemnumber,
-        prev_biblionumber   => $prev_biblionumber,   
-        starting_itemnumber => $itemnumber,
-    );
-    $result{starting_homebranch} = $start_homebranch if $start_homebranch;
-    $result{starting_location}   = $start_location   if $start_location;
-    $result{starting_ccode}         = $start_ccode      if $start_ccode;
-    return \%result;
+    my @prev_items = @{
+        $dbh->selectall_arrayref(
+            $prev_query . $query_cond . ' ORDER BY cn_sort DESC, itemnumber LIMIT ?',
+            { Slice => {} },
+            ( @params, $gap )
+        )
+    };
+    my @next_items = @{
+        $dbh->selectall_arrayref(
+            $next_query . $query_cond . ' ORDER BY cn_sort, itemnumber LIMIT ?',
+            { Slice => {} },
+            ( @params, $gap + 1 )
+        )
+    };
+
+    my $prev_item = $prev_items[-1];
+    my $next_item = $next_items[-1];
+    @next_items = splice( @next_items, 0, $num_each_side + 1 );
+    @prev_items = reverse splice( @prev_items, 0, $num_each_side );
+    my @items = ( @prev_items, @next_items );
+
+    $next_item = undef
+        if not $next_item
+            or ( $next_item->{itemnumber} == $items[-1]->{itemnumber}
+                and ( @prev_items or @next_items <= 1 )
+            );
+    $prev_item = undef
+        if not $prev_item
+            or ( $prev_item->{itemnumber} == $items[0]->{itemnumber}
+                and ( @next_items or @prev_items <= 1 )
+            );
+
+    # populate the items
+    @items = GetShelfInfo( @items );
+
+    return {
+        items               => \@items,
+        next_item           => $next_item,
+        prev_item           => $prev_item,
+        starting_homebranch => $start_homebranch,
+        starting_location   => $start_location,
+        starting_ccode      => $start_ccode,
+    };
 }
 
 }
 
-# This runs through a statement handle and pulls out all the items in it, fills
-# them up with additional info that shelves want, and returns those as a list.
+# populate an item list with its title and upc, oclc and isbn normalized.
 # Not really intended to be exported.
 sub GetShelfInfo {
 # Not really intended to be exported.
 sub GetShelfInfo {
-    my ($sth, $marcflavour) = @_;
-
-    my @items;
-    while (my $this_item = $sth->fetchrow_hashref()) {
-        my $this_biblio = GetBibData($this_item->{biblionumber});
-        next if (!defined($this_biblio));
-        $this_item->{'title'} = $this_biblio->{'title'};
+    my @items = @_;
+    my $marcflavour = C4::Context->preference("marcflavour");
+    my @valid_items;
+    for my $item ( @items ) {
+        my $this_biblio = GetBibData($item->{biblionumber});
+        next unless defined $this_biblio;
+        $item->{'title'} = $this_biblio->{'title'};
         my $this_record = GetMarcBiblio($this_biblio->{'biblionumber'});
         my $this_record = GetMarcBiblio($this_biblio->{'biblionumber'});
-        $this_item->{'browser_normalized_upc'} = GetNormalizedUPC($this_record,$marcflavour);
-        $this_item->{'browser_normalized_oclc'} = GetNormalizedOCLCNumber($this_record,$marcflavour);
-        $this_item->{'browser_normalized_isbn'} = GetNormalizedISBN(undef,$this_record,$marcflavour);
-        push @items, $this_item;
+        $item->{'browser_normalized_upc'} = GetNormalizedUPC($this_record,$marcflavour);
+        $item->{'browser_normalized_oclc'} = GetNormalizedOCLCNumber($this_record,$marcflavour);
+        $item->{'browser_normalized_isbn'} = GetNormalizedISBN(undef,$this_record,$marcflavour);
+        push @valid_items, $item;
     }
     }
-    return @items;
+    return @valid_items;
 }
 
 # Fetches some basic biblio data needed by the shelf stuff
 }
 
 # Fetches some basic biblio data needed by the shelf stuff
diff --git a/koha-tmpl/opac-tmpl/prog/en/includes/shelfbrowser.inc b/koha-tmpl/opac-tmpl/prog/en/includes/shelfbrowser.inc
new file mode 100644 (file)
index 0000000..c41647a
--- /dev/null
@@ -0,0 +1,125 @@
+[% BLOCK shelfbrowser %]
+  [% IF OpenOPACShelfBrowser %]
+    <div id="shelfbrowser">
+        <h5 style="text-align: center;">
+            [% IF ( starting_homebranch ) %]Browsing [% starting_homebranch %] Shelves[% END %]
+            [% IF ( starting_location ) %], Shelving location: [% starting_location %][% END %]
+            [% IF ( starting_ccode ) %], Collection code: [% starting_ccode %][% END %]
+            <a style="font-size: 75%;" href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% biblionumber %]" class="close_shelf" >Close shelf browser</a>
+        </h5>
+
+        <table>
+            <tr>
+                <td rowspan="2" style="width:20px;">
+                  [% IF shelfbrowser_prev_item %]
+                    <div id="browser_previous">
+                        <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% shelfbrowser_prev_item.biblionumber %]&amp;shelfbrowse_itemnumber=[% shelfbrowser_prev_item.itemnumber %]#shelfbrowser">Previous</a>
+                    </div>
+                  [% END %]
+                </td>
+
+                [% FOREACH item IN shelfbrowser_items %]
+                    <td>
+                        <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% item.biblionumber %]&amp;shelfbrowse_itemnumber=[% item.itemnumber %]#shelfbrowser">
+                            [% IF ( OPACLocalCoverImages ) %]
+                                <div title="[% item.biblionumber |url %]" class="[% item.biblionumber %] thumbnail-shelfbrowser" id="local-thumbnail-shelf-[% item.biblionumber %]"></div>
+                            [% END %]
+                            [% IF ( OPACAmazonCoverImages ) %]
+                                [% IF ( item.browser_normalized_isbn ) %]
+                                    <img border="0" src="http://images.amazon.com/images/P/[% item.browser_normalized_isbn %].01._AA75_PU_PU-5_.jpg" alt="" />
+                                [% ELSE %]
+                                    <span class="no-image">No cover image available</span>
+                                [% END %]
+                            [% END %]
+
+                            [% IF ( SyndeticsEnabled ) %]
+                                [% IF ( SyndeticsCoverImages ) %]
+                                    [% IF ( content_identifier_exists ) %]
+                                        [% IF ( using_https ) %]
+                                            <img border="0" src="https://secure.syndetics.com/index.aspx?isbn=[% item.browser_normalized_isbn %]/SC.GIF&amp;client=[% SyndeticsClientCode %][% IF ( item.browser_normalized_upc ) %]&amp;upc=[% item.browser_normalized_upc %][% END %][% IF ( item.browser_normalized_oclc ) %]&amp;oclc=[% item.browser_normalized_oclc %][% END %]&amp;type=xw10" alt="" />
+                                        [% ELSE %]
+                                            <img border="0" src="http://www.syndetics.com/index.aspx?isbn=[% item.browser_normalized_isbn %]/SC.GIF&amp;client=[% SyndeticsClientCode %][% IF ( item.browser_normalized_upc ) %]&amp;upc=[% item.browser_normalized_upc %][% END %][% IF ( item.browser_normalized_oclc ) %]&amp;oclc=[% item.browser_normalized_oclc %][% END %]&amp;type=xw10" alt="" />
+                                        [% END %]
+                                    [% ELSE %]
+                                        <span class="no-image">No cover image available</span>
+                                    [% END %]
+                                [% END %]
+                            [% END %]
+
+                            [% IF ( GoogleJackets ) %]
+                                [% IF ( item.browser_normalized_isbn ) %]
+                                    <div style="block" title="[% item.biblionumber |url %]" class="[% item.browser_normalized_isbn %]" id="gbs-thumbnail-preview[% loop.count %]"></div>
+                                [% ELSE %]
+                                    <span class="no-image">No cover image available</span>
+                                [% END %]
+                            [% END %]
+                            [% IF ( BakerTaylorEnabled ) %]
+                                [% IF ( item.browser_normalized_isbn ) %]
+                                    <img alt="See Baker &amp; Taylor" src="[% BakerTaylorImageURL |html %][% item.browser_normalized_isbn %]" />
+                                [% ELSE %]
+                                    <span class="no-image">No cover image available</span>
+                                [% END %]
+                            [% END %]
+                        </a>
+                    </td>
+                [% END %]
+
+                <td rowspan="2" style="width:20px;">
+                  [% IF shelfbrowser_next_item %]
+                    <div id="browser_next">
+                        <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% shelfbrowser_prev_item.biblionumber %]&amp;shelfbrowse_itemnumber=[% shelfbrowser_prev_item.itemnumber %]#shelfbrowser">Next</a>
+                    </div>
+                  [% END %]
+                </td>
+            </tr>
+
+            <tr>
+                [% FOREACH item IN shelfbrowser_items %]
+                    <td class="top">
+                        [% item.itemcallnumber %]
+                        <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% item.biblionumber %]&amp;shelfbrowse_itemnumber=[% item.itemnumber %]#shelfbrowser">[% item.title |html %]</a>
+                    </td>
+                [% END %]
+            </tr>
+        </table>
+    </div>
+    <script type="text/javascript">
+      $(document).ready(function(){
+        $(".close_shelf").click(function(e){
+            e.preventDefault();
+            $("#shelfbrowser").hide();
+        });
+        [% IF shelfbrowser_prev_item.itemnumber %]
+          $("#browser_previous a").click(function(e){
+            e.preventDefault();
+            $.ajax({
+                    url: "/cgi-bin/koha/svc/shelfbrowser.pl",
+                type: "POST",
+                data: {
+                    "shelfbrowse_itemnumber": [% shelfbrowser_prev_item.itemnumber %]
+                },
+                success: function(data){
+                    $("#shelfbrowser").replaceWith(data);
+                }
+            });
+          });
+        [% END %]
+        [% IF shelfbrowser_next_item.itemnumber %]
+          $("#browser_next a").click(function(e){
+            e.preventDefault();
+            $.ajax({
+                url: "/cgi-bin/koha/svc/shelfbrowser.pl",
+                type: "POST",
+                data: {
+                    "shelfbrowse_itemnumber": [% shelfbrowser_next_item.itemnumber %]
+                },
+                success: function(data){
+                    $("#shelfbrowser").replaceWith(data);
+                }
+            });
+          });
+        [% END %]
+      });
+    </script>
+  [% END %]
+[% END %][%# end of shelfbrowser block %]
index d6520f5..3f7053d 100644 (file)
@@ -1068,6 +1068,7 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
     [% END %]
 [% END %]
 
     [% END %]
 [% END %]
 
+[% PROCESS 'shelfbrowser.inc' %]
 [% INCLUDE shelfbrowser tab='holdings' %]
 <br clear="all" />
 </div>
 [% INCLUDE shelfbrowser tab='holdings' %]
 <br clear="all" />
 </div>
@@ -1588,137 +1589,3 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
            [% END %]</tbody>
        </table>
 [% END %][%# end of items_table block %]
            [% END %]</tbody>
        </table>
 [% END %][%# end of items_table block %]
-
-[% BLOCK shelfbrowser %]
-    [% IF ( OpenOPACShelfBrowser and shelfbrowser_tab == tab) %]
-        <div id="shelfbrowser">
-            <h5 style="text-align: center;">
-                [% IF ( starting_homebranch ) %]Browsing [% starting_homebranch %] Shelves[% END %]
-                [% IF ( starting_location ) %], Shelving location: [% starting_location %][% END %]
-                [% IF ( starting_ccode ) %], Collection code: [% starting_ccode %][% END %]
-                <a style="font-size: 75%;" href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% biblionumber %]">Close shelf browser</a>
-            </h5>
-
-            <table>
-                <tr>
-                    <td rowspan="2" style="width:20px;">
-                        <div id="browser_previous">
-                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% IF ( shelfbrowser_prev_biblionumber ) %][% shelfbrowser_prev_biblionumber %][% ELSE %][% biblionumber %][% END %]&amp;shelfbrowse_itemnumber=[% shelfbrowser_prev_itemnumber %]#shelfbrowser">Previous</a>
-                        </div>
-                    </td>
-                    [% FOREACH PREVIOUS_SHELF_BROWS IN PREVIOUS_SHELF_BROWSE %]
-                        <td>
-                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% PREVIOUS_SHELF_BROWS.biblionumber %]&amp;shelfbrowse_itemnumber=[% PREVIOUS_SHELF_BROWS.itemnumber %]#shelfbrowser">
-                                [% IF ( OPACLocalCoverImages ) %]
-                                    <div title="[% PREVIOUS_SHELF_BROWS.biblionumber |url %]" class="[% PREVIOUS_SHELF_BROWS.biblionumber %] thumbnail-shelfbrowser" id="local-thumbnail-shelf-[% PREVIOUS_SHELF_BROWS.biblionumber %]"></div>
-                                [% END %]
-                                [% IF ( OPACAmazonCoverImages ) %]
-                                    [% IF ( PREVIOUS_SHELF_BROWS.browser_normalized_isbn ) %]
-                                        <img border="0" src="http://images.amazon.com/images/P/[% PREVIOUS_SHELF_BROWS.browser_normalized_isbn %].01._AA75_PU_PU-5_.jpg" alt="" />
-                                    [% ELSE %]
-                                        <span class="no-image">No cover image available</span>
-                                    [% END %]
-                                [% END %]
-                                [% IF ( SyndeticsEnabled ) %]
-                                    [% IF ( SyndeticsCoverImages ) %]
-                                        [% IF ( content_identifier_exists ) %]
-                                            [% IF ( using_https ) %]
-                                                <img border="0" src="https://secure.syndetics.com/index.aspx?isbn=[% PREVIOUS_SHELF_BROWS.browser_normalized_isbn %]/SC.GIF&amp;client=[% SyndeticsClientCode %][% IF ( PREVIOUS_SHELF_BROWS.browser_normalized_upc ) %]&amp;upc=[% PREVIOUS_SHELF_BROWS.browser_normalized_upc %][% END %][% IF ( PREVIOUS_SHELF_BROWS.browser_normalized_oclc ) %]&amp;oclc=[% PREVIOUS_SHELF_BROWS.browser_normalized_oclc %][% END %]&amp;type=xw10" alt="" />
-                                            [% ELSE %]
-                                                <img border="0" src="http://www.syndetics.com/index.aspx?isbn=[% PREVIOUS_SHELF_BROWS.browser_normalized_isbn %]/SC.GIF&amp;client=[% SyndeticsClientCode %][% IF ( PREVIOUS_SHELF_BROWS.browser_normalized_upc ) %]&amp;upc=[% PREVIOUS_SHELF_BROWS.browser_normalized_upc %][% END %][% IF ( PREVIOUS_SHELF_BROWS.browser_normalized_oclc ) %]&amp;oclc=[% PREVIOUS_SHELF_BROWS.browser_normalized_oclc %][% END %]&amp;type=xw10" alt="" />
-                                            [% END %]
-                                        [% ELSE %]
-                                            <span class="no-image">No cover image available</span>
-                                        [% END %]
-                                    [% END %]
-                                [% END %]
-                                [% IF ( GoogleJackets ) %]
-                                    [% IF ( PREVIOUS_SHELF_BROWS.browser_normalized_isbn ) %]
-                                        <div style="block" title="[% PREVIOUS_SHELF_BROWS.biblionumber |url %]" class="[% PREVIOUS_SHELF_BROWS.browser_normalized_isbn %]" id="gbs-thumbnail-preview[% loop.count %]"></div>
-                                    [% ELSE %]
-                                        <span class="no-image">No cover image available</span>
-                                    [% END %]
-                                [% END %]
-                                [% IF ( BakerTaylorEnabled ) %]
-                                    [% IF ( PREVIOUS_SHELF_BROWS.browser_normalized_isbn ) %]
-                                        <img alt="See Baker &amp; Taylor" src="[% BakerTaylorImageURL |html %][% PREVIOUS_SHELF_BROWS.browser_normalized_isbn %]" />
-                                    [% ELSE %]
-                                        <span class="no-image">No cover image available</span>
-                                    [% END %]
-                                [% END %]
-                            </a>
-                        </td>
-                    [% END %]
-
-                    [% FOREACH NEXT_SHELF_BROWS IN NEXT_SHELF_BROWSE %]
-                        <td>
-                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% NEXT_SHELF_BROWS.biblionumber %]&amp;shelfbrowse_itemnumber=[% NEXT_SHELF_BROWS.itemnumber %]#shelfbrowser">
-                                [% IF ( OPACLocalCoverImages ) %]
-                                    <div title="[% NEXT_SHELF_BROWS.biblionumber |url %]" class="[% NEXT_SHELF_BROWS.biblionumber %] thumbnail-shelfbrowser" id="local-thumbnail-shelf-[% NEXT_SHELF_BROWS.biblionumber %]"></div>
-                                [% END %]
-                                [% IF ( OPACAmazonCoverImages ) %]
-                                    [% IF ( NEXT_SHELF_BROWS.browser_normalized_isbn ) %]
-                                        <img border="0" src="http://images.amazon.com/images/P/[% NEXT_SHELF_BROWS.browser_normalized_isbn %].01._AA75_PU_PU-5_.jpg" alt="" />
-                                    [% ELSE %]
-                                        <span class="no-image">No cover image available</span>
-                                    [% END %]
-                                [% END %]
-
-                                [% IF ( SyndeticsEnabled ) %]
-                                    [% IF ( SyndeticsCoverImages ) %]
-                                        [% IF ( content_identifier_exists ) %]
-                                            [% IF ( using_https ) %]
-                                                <img border="0" src="https://secure.syndetics.com/index.aspx?isbn=[% NEXT_SHELF_BROWS.browser_normalized_isbn %]/SC.GIF&amp;client=[% SyndeticsClientCode %][% IF ( NEXT_SHELF_BROWS.browser_normalized_upc ) %]&amp;upc=[% NEXT_SHELF_BROWS.browser_normalized_upc %][% END %][% IF ( NEXT_SHELF_BROWS.browser_normalized_oclc ) %]&amp;oclc=[% NEXT_SHELF_BROWS.browser_normalized_oclc %][% END %]&amp;type=xw10" alt="" />
-                                            [% ELSE %]
-                                                <img border="0" src="http://www.syndetics.com/index.aspx?isbn=[% NEXT_SHELF_BROWS.browser_normalized_isbn %]/SC.GIF&amp;client=[% SyndeticsClientCode %][% IF ( NEXT_SHELF_BROWS.browser_normalized_upc ) %]&amp;upc=[% NEXT_SHELF_BROWS.browser_normalized_upc %][% END %][% IF ( NEXT_SHELF_BROWS.browser_normalized_oclc ) %]&amp;oclc=[% NEXT_SHELF_BROWS.browser_normalized_oclc %][% END %]&amp;type=xw10" alt="" />
-                                            [% END %]
-                                        [% ELSE %]
-                                            <span class="no-image">No cover image available</span>
-                                        [% END %]
-                                    [% END %]
-                                [% END %]
-
-                                [% IF ( GoogleJackets ) %]
-                                    [% IF ( NEXT_SHELF_BROWS.browser_normalized_isbn ) %]
-                                        <div style="block" title="[% NEXT_SHELF_BROWS.biblionumber |url %]" class="[% NEXT_SHELF_BROWS.browser_normalized_isbn %]" id="gbs-thumbnail-preview[% loop.count %]"></div>
-                                    [% ELSE %]
-                                        <span class="no-image">No cover image available</span>
-                                    [% END %]
-                                [% END %]
-                                [% IF ( BakerTaylorEnabled ) %]
-                                    [% IF ( NEXT_SHELF_BROWS.browser_normalized_isbn ) %]
-                                        <img alt="See Baker &amp; Taylor" src="[% BakerTaylorImageURL |html %][% NEXT_SHELF_BROWS.browser_normalized_isbn %]" />
-                                    [% ELSE %]
-                                        <span class="no-image">No cover image available</span>
-                                    [% END %]
-                                [% END %]
-                            </a>
-                        </td>
-                    [% END %]
-
-                    <td rowspan="2">
-                        <div id="browser_next">
-                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% IF ( shelfbrowser_next_biblionumber ) %][% shelfbrowser_next_biblionumber %][% ELSE %][% biblionumber %][% END %]&amp;shelfbrowse_itemnumber=[% shelfbrowser_next_itemnumber %]#shelfbrowser">Next</a>
-                        </div>
-                    </td>
-                </tr>
-
-                <tr>
-                    [% FOREACH PREVIOUS_SHELF_BROWS IN PREVIOUS_SHELF_BROWSE %]
-                        <td class="top">
-                            [% PREVIOUS_SHELF_BROWS.itemcallnumber %]
-                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% PREVIOUS_SHELF_BROWS.biblionumber %]&amp;shelfbrowse_itemnumber=[% PREVIOUS_SHELF_BROWS.itemnumber %]#shelfbrowser">[% PREVIOUS_SHELF_BROWS.title |html %]</a>
-                        </td>
-                    [% END %]
-
-                    [% FOREACH NEXT_SHELF_BROWS IN NEXT_SHELF_BROWSE %]
-                        <td class="top" style="width:20px;">
-                            [% NEXT_SHELF_BROWS.itemcallnumber %]
-                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% NEXT_SHELF_BROWS.biblionumber %]&amp;shelfbrowse_itemnumber=[% NEXT_SHELF_BROWS.itemnumber %]#shelfbrowser">[% NEXT_SHELF_BROWS.title |html %]</a>
-                        </td>
-                    [% END %]
-                </tr>
-            </table>
-        </div>
-    [% END %]
-[% END %][%# end of shelfbrowser block %]
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/svc/shelfbrowser.tt b/koha-tmpl/opac-tmpl/prog/en/modules/svc/shelfbrowser.tt
new file mode 100644 (file)
index 0000000..50e2b40
--- /dev/null
@@ -0,0 +1,2 @@
+[% PROCESS 'shelfbrowser.inc' %]
+[% INCLUDE shelfbrowser %]
index eb6b77f..0b5ef8c 100755 (executable)
@@ -928,19 +928,15 @@ if (C4::Context->preference("OPACShelfBrowser")) {
     my $starting_itemnumber = $query->param('shelfbrowse_itemnumber');
     if (defined($starting_itemnumber)) {
         $template->param( OpenOPACShelfBrowser => 1) if $starting_itemnumber;
     my $starting_itemnumber = $query->param('shelfbrowse_itemnumber');
     if (defined($starting_itemnumber)) {
         $template->param( OpenOPACShelfBrowser => 1) if $starting_itemnumber;
-        my $nearby = GetNearbyItems($starting_itemnumber,3);
+        my $nearby = GetNearbyItems($starting_itemnumber);
 
         $template->param(
             starting_homebranch => $nearby->{starting_homebranch}->{description},
             starting_location => $nearby->{starting_location}->{description},
             starting_ccode => $nearby->{starting_ccode}->{description},
 
         $template->param(
             starting_homebranch => $nearby->{starting_homebranch}->{description},
             starting_location => $nearby->{starting_location}->{description},
             starting_ccode => $nearby->{starting_ccode}->{description},
-            starting_itemnumber => $nearby->{starting_itemnumber},
-            shelfbrowser_prev_itemnumber => $nearby->{prev_itemnumber},
-            shelfbrowser_next_itemnumber => $nearby->{next_itemnumber},
-            shelfbrowser_prev_biblionumber => $nearby->{prev_biblionumber},
-            shelfbrowser_next_biblionumber => $nearby->{next_biblionumber},
-            PREVIOUS_SHELF_BROWSE => $nearby->{prev},
-            NEXT_SHELF_BROWSE => $nearby->{next},
+            shelfbrowser_prev_item => $nearby->{prev_item},
+            shelfbrowser_next_item => $nearby->{next_item},
+            shelfbrowser_items => $nearby->{items},
         );
 
         # in which tab shelf browser should open ?
         );
 
         # in which tab shelf browser should open ?
diff --git a/opac/svc/shelfbrowser.pl b/opac/svc/shelfbrowser.pl
new file mode 100755 (executable)
index 0000000..33c9cde
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+use Modern::Perl;
+use CGI;
+
+use C4::Auth;
+use C4::Context;
+use C4::Output;
+use C4::ShelfBrowser;
+
+my $cgi = new CGI;
+
+my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
+    {
+        template_name   => "svc/shelfbrowser.tt",
+        query           => $cgi,
+        type            => "opac",
+        authnotrequired => ( C4::Context->preference("OpacPublic") ? 1 : 0 ),
+        flagsrequired   => { borrow => 1 },
+    }
+);
+
+# Shelf Browser Stuff
+if (C4::Context->preference("OPACShelfBrowser")) {
+    my $starting_itemnumber = $cgi->param('shelfbrowse_itemnumber');
+    if (defined($starting_itemnumber)) {
+        my $nearby = GetNearbyItems($starting_itemnumber);
+
+        $template->param(
+            starting_homebranch => $nearby->{starting_homebranch}->{description},
+            starting_location => $nearby->{starting_location}->{description},
+            starting_ccode => $nearby->{starting_ccode}->{description},
+            shelfbrowser_prev_item => $nearby->{prev_item},
+            shelfbrowser_next_item => $nearby->{next_item},
+            shelfbrowser_items => $nearby->{items},
+            OpenOPACShelfBrowser => 1,
+        );
+    }
+}
+
+print $template->output;
diff --git a/t/db_dependent/ShelfBrowser.t b/t/db_dependent/ShelfBrowser.t
new file mode 100644 (file)
index 0000000..0730f4a
--- /dev/null
@@ -0,0 +1,212 @@
+#!/usr/bin/perl
+
+use Modern::Perl;
+use Test::More tests => 74;
+use List::Util qw( shuffle );
+use MARC::Field;
+use MARC::Record;
+
+use C4::Biblio;
+use C4::Context;
+use C4::Items;
+
+use_ok('C4::ShelfBrowser');
+
+my $dbh = C4::Context->dbh;
+$dbh->{AutoCommit} = 0;
+$dbh->{RaiseError} = 1;
+
+$dbh->do(q|DELETE FROM reserves|);
+$dbh->do(q|DELETE FROM issues|);
+$dbh->do(q|DELETE FROM items|);
+
+my $cn;
+
+# 100.100 150.100 200.100 210.100 300.000 320.000 400.100 410.100 500.100 510.100 520.100 600.000 610.000 700.100 710.100 720.100 730.100 740.100 750.100
+my @callnumbers = qw(
+    100.100
+    150.100
+    200.100
+    210.100
+    300.000
+    320.000
+    400.100
+    410.100
+    500.100
+    510.100
+    520.100
+    600.000
+    610.000
+    700.100
+    710.100
+    720.100
+    730.100
+    740.100
+    750.100
+);
+
+my $record = MARC::Record->new();
+$record->append_fields(
+    MARC::Field->new('100', ' ', ' ', a => 'Donald E. Knuth.'),
+    MARC::Field->new('245', ' ', ' ', a => 'The art of computer programming'),
+);
+my ( $biblionumber, undef, undef ) = C4::Biblio::AddBiblio($record, '');
+
+for my $callnumber ( shuffle @callnumbers ) {
+    my ( $biblionumber, undef, $itemnumber ) = C4::Items::AddItem({
+        homebranch => 'CPL',
+        holdingbranch => 'CPL',
+        itemcallnumber => $callnumber,
+    }, $biblionumber);
+    $cn->{$callnumber} = {
+        biblionumber => $biblionumber,
+        itemnumber => $itemnumber,
+        itemcallnumber => $callnumber,
+    }
+}
+
+my $nearby;
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'500.100'}{itemnumber} );
+# We have
+# < 320.000 400.100 410.100 500.100 510.100 520.100 600.000 >
+#      6       7       8      [9]       10      11      12
+# Clicking on previous, we want a link to 150.100
+is( $nearby->{prev_item}{itemcallnumber}, '150.100', "Simple case: previous link 1/2" );
+is( $nearby->{prev_item}{itemnumber}, $cn->{'150.100'}{itemnumber}, "Simple case: previous link 2/2" );
+# Clicking on next, we want a link to 730.100
+is( $nearby->{next_item}{itemcallnumber}, '720.100', "Simple case: next link 1/2" );
+is( $nearby->{next_item}{itemnumber}, $cn->{'720.100'}{itemnumber}, "Simple case: next link 2/2" );
+
+is( $nearby->{items}[0]{itemcallnumber}, '320.000', "Simple case: item 1");
+is( $nearby->{items}[1]{itemcallnumber}, '400.100', "Simple case: item 2");
+is( $nearby->{items}[2]{itemcallnumber}, '410.100', "Simple case: item 3");
+is( $nearby->{items}[3]{itemcallnumber}, '500.100', "Simple case: item 4");
+is( $nearby->{items}[4]{itemcallnumber}, '510.100', "Simple case: item 5");
+is( $nearby->{items}[5]{itemcallnumber}, '520.100', "Simple case: item 6");
+is( $nearby->{items}[6]{itemcallnumber}, '600.000', "Simple case: item 7");
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'500.100'}{itemnumber}, 2, 3 );
+# We have
+# < 400.100 410.100 500.100 510.100 520.100 >
+#      7       8      [9]       10      11
+# Clicking on previous, we want a link to 320.000
+is( $nearby->{prev_item}{itemcallnumber}, '320.000', "Test gap: previous link 1/2" );
+is( $nearby->{prev_item}{itemnumber}, $cn->{'320.000'}{itemnumber}, "Test gap: previous link 2/2" );
+# Clicking on next, we want a link to 600.000
+is( $nearby->{next_item}{itemcallnumber}, '600.000', "Test gap: next link 1/2" );
+is( $nearby->{next_item}{itemnumber}, $cn->{'600.000'}{itemnumber}, "Test gap: next link 2/2" );
+
+is( scalar( @{$nearby->{items}} ), 5, "Test gap: got 5 items" );
+is( $nearby->{items}[0]{itemcallnumber}, '400.100', "Test gap: item 1");
+is( $nearby->{items}[1]{itemcallnumber}, '410.100', "Test gap: item 2");
+is( $nearby->{items}[2]{itemcallnumber}, '500.100', "Test gap: item 3");
+is( $nearby->{items}[3]{itemcallnumber}, '510.100', "Test gap: item 4");
+is( $nearby->{items}[4]{itemcallnumber}, '520.100', "Test gap: item 5");
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'300.000'}{itemnumber} );
+# We have
+# < 150.100 200.100 210.100 300.000 320.000 400.100 410.100 >
+#      2       3       4      [5]      6       7       8
+# Clicking on previous, we want a link to 100.100
+is( $nearby->{prev_item}{itemcallnumber}, '100.100', "Test start shelf: previous link 1/2" );
+is( $nearby->{prev_item}{itemnumber}, $cn->{'100.100'}{itemnumber}, "Test start shelf: previous link 2/2" );
+# Clicking on next, we want a link to 600.000
+is( $nearby->{next_item}{itemcallnumber}, '600.000', "Test start shelf: next link 1/2" );
+is( $nearby->{next_item}{itemnumber}, $cn->{'600.000'}{itemnumber}, "Test start shelf: next link 2/2" );
+
+is( $nearby->{items}[0]{itemcallnumber}, '150.100', "Test start shelf: item 1");
+is( $nearby->{items}[1]{itemcallnumber}, '200.100', "Test start shelf: item 2");
+is( $nearby->{items}[2]{itemcallnumber}, '210.100', "Test start shelf: item 3");
+is( $nearby->{items}[3]{itemcallnumber}, '300.000', "Test start shelf: item 4");
+is( $nearby->{items}[4]{itemcallnumber}, '320.000', "Test start shelf: item 5");
+is( $nearby->{items}[5]{itemcallnumber}, '400.100', "Test start shelf: item 6");
+is( $nearby->{items}[6]{itemcallnumber}, '410.100', "Test start shelf: item 7");
+
+
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'100.100'}{itemnumber} );
+# We have
+# 100.100 150.100 200.100 210.100 >
+#   [1]       2       3       4
+# There is no previous link
+is( $nearby->{prev_item}, undef, "Test first item on a shelf: no previous link" );
+# Clicking on next, we want a link to 410.100
+is( $nearby->{next_item}{itemcallnumber}, '410.100', "Test first item on a shelf: next link 1/2" );
+is( $nearby->{next_item}{itemnumber}, $cn->{'410.100'}{itemnumber}, "Test first item on a shelf: next link 2/2" );
+
+is( scalar( @{$nearby->{items}} ), 4, "Test first item on a shelf: There are 4 items displayed" );
+is( $nearby->{items}[0]{itemcallnumber}, '100.100', "Test first item on a shelf: item 1");
+is( $nearby->{items}[1]{itemcallnumber}, '150.100', "Test first item on a shelf: item 2");
+is( $nearby->{items}[2]{itemcallnumber}, '200.100', "Test first item on a shelf: item 3");
+is( $nearby->{items}[3]{itemcallnumber}, '210.100', "Test first item on a shelf: item 4");
+
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'150.100'}{itemnumber} );
+# We have
+# 100.100 150.100 200.100 210.100 300.000 >
+#    1      [2]       3       4      5
+# There is no previous link
+is( $nearby->{prev_item}, undef, "Test second item on a shelf: no previous link" );
+# Clicking on next, we want a link to 500.100
+is( $nearby->{next_item}{itemcallnumber}, '500.100', "Test second item on a shelf: next link 1/2" );
+is( $nearby->{next_item}{itemnumber}, $cn->{'500.100'}{itemnumber}, "Test second item on a shelf: next link 2/2" );
+
+is( scalar( @{$nearby->{items}} ), 5, "Test second item on a shelf: got 5 items" );
+is( $nearby->{items}[0]{itemcallnumber}, '100.100', "Test second item on a shelf: item 1");
+is( $nearby->{items}[1]{itemcallnumber}, '150.100', "Test second item on a shelf: item 2");
+is( $nearby->{items}[2]{itemcallnumber}, '200.100', "Test second item on a shelf: item 3");
+is( $nearby->{items}[3]{itemcallnumber}, '210.100', "Test second item on a shelf: item 4");
+is( $nearby->{items}[4]{itemcallnumber}, '300.000', "Test second item on a shelf: item 5");
+
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'710.100'}{itemnumber} );
+# We have
+# < 600.000 610.000 700.100 710.100 720.100 730.100 740.100 >
+#      12      13      14     [15]     16      17      18
+# Clicking on previous, we want a link to 410.100
+is( $nearby->{prev_item}{itemcallnumber}, '410.100', "Test end shelf: previous link 1/2" );
+is( $nearby->{prev_item}{itemnumber}, $cn->{'410.100'}{itemnumber}, "Test end shelf: previous link 2/2" );
+# Clicking on next, we want a link to 730.100
+is( $nearby->{next_item}{itemcallnumber}, '750.100', "Test end shelf: next link is a link to the last item 1/2" );
+is( $nearby->{next_item}{itemnumber}, $cn->{'750.100'}{itemnumber}, "Test end shelf: next link is a link to the last item 2/2" );
+
+is( $nearby->{items}[0]{itemcallnumber}, '600.000', "Test end shelf: item 1");
+is( $nearby->{items}[1]{itemcallnumber}, '610.000', "Test end shelf: item 2");
+is( $nearby->{items}[2]{itemcallnumber}, '700.100', "Test end shelf: item 3");
+is( $nearby->{items}[3]{itemcallnumber}, '710.100', "Test end shelf: item 4");
+is( $nearby->{items}[4]{itemcallnumber}, '720.100', "Test end shelf: item 5");
+is( $nearby->{items}[5]{itemcallnumber}, '730.100', "Test end shelf: item 6");
+is( $nearby->{items}[6]{itemcallnumber}, '740.100', "Test end shelf: item 7");
+
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'740.100'}{itemnumber} );
+# We have
+# < 710.100 720.100 730.100 740.100 750.100
+#      15      16      17     [18]     19
+# Clicking on previous, we want a link to
+is( $nearby->{prev_item}{itemcallnumber}, '520.100', "Test end of the shelf: previous link 1/2" );
+is( $nearby->{prev_item}{itemnumber}, $cn->{'520.100'}{itemnumber}, "Test end of the shelf: previous link 2/2" );
+# No next link
+is( $nearby->{next_item}, undef, "Test end of the shelf: no next link" );
+
+is( scalar( @{$nearby->{items}} ), 5, "Test end of the shelf: got 5 items" );
+is( $nearby->{items}[0]{itemcallnumber}, '710.100', "Test end of the shelf: item 1");
+is( $nearby->{items}[1]{itemcallnumber}, '720.100', "Test end of the shelf: item 2");
+is( $nearby->{items}[2]{itemcallnumber}, '730.100', "Test end of the shelf: item 3");
+is( $nearby->{items}[3]{itemcallnumber}, '740.100', "Test end of the shelf: item 4");
+is( $nearby->{items}[4]{itemcallnumber}, '750.100', "Test end of the shelf: item 5");
+
+$nearby = C4::ShelfBrowser::GetNearbyItems( $cn->{'750.100'}{itemnumber} );
+# We have
+# < 720.100 730.100 740.100 750.100
+#      16      17      18     [19]
+# Clicking on previous, we want a link to
+is( $nearby->{prev_item}{itemcallnumber}, '600.000', "Test last item of the shelf: previous link 1/2" );
+is( $nearby->{prev_item}{itemnumber}, $cn->{'600.000'}{itemnumber}, "Test last item of the shelf: previous link 2/2" );
+# No next link
+is( $nearby->{next_item}, undef, "Test end of the shelf: no next link" );
+
+is( scalar( @{$nearby->{items}} ), 4, "Test last item of the shelf: got 4 items" );
+
+$dbh->rollback;