--- /dev/null
+#!/usr/bin/perl
+
+package C4::ShelfBrowser;
+
+# Copyright 2010 Catalyst IT
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use strict;
+use warnings;
+
+use C4::Biblio;
+use C4::Branch;
+use C4::Context;
+use C4::Koha;
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+BEGIN {
+ $VERSION = 3.02;
+ require Exporter;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ &GetNearbyItems
+ );
+ @EXPORT_OK = qw(
+ );
+}
+
+=head1 NAME
+
+C4::ShelfBrowser - functions that deal with the shelf browser feature found in
+the OPAC.
+
+=head1 SYNOPSIS
+
+ use C4::ShelfBrowser;
+
+=head1 DESCRIPTION
+
+This module provides functions to get items nearby to another item, for use
+in the shelf browser function.
+
+'Nearby' is controlled by a handful of system preferences that specify what
+to take into account.
+
+=head1 FUNCTIONS
+
+=head2 GetNearbyItems($itemnumber, [$num_each_side])
+
+ $nearby = GetNearbyItems($itemnumber, [$num_each_side]);
+
+ @next = @{ $nearby->{next} };
+ @prev = @{ $nearby->{prev} };
+
+ foreach (@next) {
+ # These won't format well like this, but here are the fields
+ print $_->{title};
+ print $_->{biblionumber};
+ print $_->{itemnumber};
+ print $_->{browser_normalized_upc};
+ print $_->{browser_normalized_oclc};
+ print $_->{browser_normalized_isbn};
+ }
+
+ # 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};
+
+ # These will be undef if the values are not used to calculate the
+ # nearby items.
+ print $nearby->{starting_homebranch}->{code};
+ print $nearby->{starting_homebranch}->{description};
+ print $nearby->{starting_location}->{code};
+ print $nearby->{starting_location}->{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.
+
+It automatically applies the following user preferences to work out how to
+calculate things: C<ShelfBrowserUsesLocation>, C<ShelfBrowserUsesHomeBranch>,
+C<ShelfBrowserUsesCcode>.
+
+The option C<$num_each_side> value determines how many items will be fetched
+each side of the supplied item. Note that the item itself is the first entry
+in the 'next' set, and counts towards this limit (this is to keep the
+behaviour consistant with the code that this is a refactor of.) Default is
+3.
+
+This will throw an exception if something went wrong.
+
+=cut
+
+sub GetNearbyItems {
+ my ($itemnumber, $num_each_side) = @_;
+ $num_each_side ||= 3;
+
+ 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=?");
+ $sth_get_item_details->execute($itemnumber);
+ my $item_details_result = $sth_get_item_details->fetchrow_hashref();
+ die "Unable to find item '$itemnumber' for shelf browser" if (!$sth_get_item_details);
+ my $start_cn_sort = $item_details_result->{'cn_sort'};
+
+ my ($start_homebranch, $start_location, $start_ccode);
+ if (C4::Context->preference('ShelfBrowserUsesHomeBranch') &&
+ defined($item_details_result->{'homebranch'})) {
+ $start_homebranch->{code} = $item_details_result->{'homebranch'};
+ $start_homebranch->{description} = $branches->{$item_details_result->{'homebranch'}}{branchname};
+ }
+ if (C4::Context->preference('ShelfBrowserUsesLocation') &&
+ defined($item_details_result->{'location'})) {
+ $start_location->{code} = $item_details_result->{'location'};
+ $start_location->{description} = GetAuthorisedValueDesc('','',$item_details_result->{'location'},'','','LOC','opac');
+ }
+ if (C4::Context->preference('ShelfBrowserUsesCcode') &&
+ defined($item_details_result->{'ccode'})) {
+ $start_ccode->{code} = $item_details_result->{'ccode'};
+ $start_ccode->{description} = GetAuthorisedValueDesc('', '', $item_details_result->{'ccode'}, '', '', 'CCODE', 'opac');
+ }
+
+ # Build the query for previous and next items
+ my $prev_query ='
+ SELECT *
+ FROM items
+ WHERE
+ ((cn_sort = ? AND itemnumber < ?) OR cn_sort < ?) ';
+ my $next_query ='
+ SELECT *
+ FROM items
+ WHERE
+ ((cn_sort = ? AND itemnumber >= ?) OR cn_sort > ?) ';
+ my @params;
+ my $query_cond;
+ push @params, ($start_cn_sort, $itemnumber, $start_cn_sort);
+ if ($start_homebranch) {
+ $query_cond .= 'AND homebranch = ? ';
+ push @params, $start_homebranch->{code};
+ }
+ if ($start_location) {
+ $query_cond .= 'AND location = ? ';
+ push @params, $start_location->{code};
+ }
+ if ($start_ccode) {
+ $query_cond .= 'AND ccode = ? ';
+ 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_items[-1]->{itemnumber} if @next_items;
+ my $next_biblionumber = $next_items[-1]->{biblionumber} if @next_items;
+
+ my $prev_itemnumber = $prev_items[0]->{itemnumber} if @prev_items;
+ my $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;
+}
+
+# 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.
+# 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 $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;
+ }
+ return @items;
+}
+
+# Fetches some basic biblio data needed by the shelf stuff
+sub GetBibData {
+ my ($bibnum) = @_;
+
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("SELECT biblionumber, title FROM biblio WHERE biblionumber=?");
+ $sth->execute($bibnum);
+ my $bib = $sth->fetchrow_hashref();
+ return $bib;
+}
+
+1;
INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ILS-DI:AuthorizedIPs','','.','Restricts usage of ILS-DI to some IPs','Free');
INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('OverduesBlockCirc','noblock','When checking out an item should overdues block checkout, generate a confirmation dialogue, or allow checkout','noblock|confirmation|block','Choice');
INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('DisplayMultiPlaceHold','1','Display the ability to place multiple holds or not','','YesNo');
+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('ShelfBrowserUsesLocation','1','Use the item location when finding items for the shelf browser.','1','YesNo');
+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('ShelfBrowserUsesHomeBranch','1','Use the item home branch when finding items for the shelf browser.','1','YesNo');
+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('ShelfBrowserUsesCcode','1','Use the item collection code when finding items for the shelf browser.','0','YesNo');
if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
$dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('OpacPublic','1','If set to OFF and user is not logged in, all OPAC pages require authentication, and OPAC searchbar is removed)','','YesNo')");
print "Upgrade to $DBversion done (added 'OpacPublic' syspref)\n";
+ SetVersion ($DBversion);
+}
+
+$DBversion = "3.03.00.xxx";
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+ $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('ShelfBrowserUsesLocation','1','Use the item location when finding items for the shelf browser.','1','YesNo')");
+ $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('ShelfBrowserUsesHomeBranch','1','Use the item home branch when finding items for the shelf browser.','1','YesNo')");
+ $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('ShelfBrowserUsesCcode','0','Use the item collection code when finding items for the shelf browser.','1','YesNo')");
+ print "Upgrade to $DBversion done (Add flexible shelf browser constraints)\n";
SetVersion ($DBversion);
}
no: "Don't allow"
- patrons to see what books they have checked out in the past.
-
- - pref: OPACShelfBrowser
- choices:
- yes: Show
- no: "Don't show"
- - "a shelf browser on item details pages, allowing patrons to see what's near that item on the shelf. Note that this uses up a fairly large amount of resources on your server, and should be avoided if your collection has a large number of items."
- -
- pref: OpacTopissue
choices:
yes: Allow
yes: Keep
no: "Don't keep"
- patron search history in the OPAC.
+ Shelf Browser:
+ -
+ - pref: OPACShelfBrowser
+ choices:
+ yes: Show
+ no: "Don't show"
+ - "a shelf browser on item details pages, allowing patrons to see what's near that item on the shelf. Note that this uses up a fairly large amount of resources on your server, and should be avoided if your collection has a large number of items."
+ -
+ - pref: ShelfBrowserUsesLocation
+ default: 1
+ choices:
+ yes: Use
+ no: "Don't use"
+ - "the item location when finding items for the shelf browser."
+ -
+ - pref: ShelfBrowserUsesHomeBranch
+ default: 1
+ choices:
+ yes: Use
+ no: "Don't use"
+ - "the item home branch when finding items for the shelf browser."
+ -
+ - pref: ShelfBrowserUsesCcode
+ default: 0
+ choices:
+ yes: Use
+ no: "Don't use"
+ - "the item collection code when finding items for the shelf browser."
<!-- TMPL_IF NAME="OpenOPACShelfBrowser" -->
<div id="shelfbrowser">
-<h5 style="text-align: center;"><!-- TMPL_IF NAME="starting_homebranch" -->Browsing <!-- TMPL_VAR NAME="starting_homebranch" --> Shelves<!-- /TMPL_IF --><!-- TMPL_IF NAME="starting_location" -->, Shelving Location:</span><!-- TMPL_VAR NAME="starting_location" --> <!-- /TMPL_IF --> <a style="font-size: 75%;" href="/cgi-bin/koha/opac-detail.pl?biblionumber=<!-- TMPL_VAR NAME="biblionumber" -->">Close Shelf Browser</a></h5>
+<h5 style="text-align: center;"><!-- TMPL_IF NAME="starting_homebranch" -->Browsing <!-- TMPL_VAR NAME="starting_homebranch" --> Shelves<!-- /TMPL_IF --><!-- TMPL_IF NAME="starting_location" -->, Shelving Location: <!-- TMPL_VAR NAME="starting_location" --><!-- /TMPL_IF --><!-- TMPL_IF name="starting_ccode" -->, Collection Code: <!-- TMPL_VAR name="starting_ccode" --><!-- /TMPL_IF --> <a style="font-size: 75%;" href="/cgi-bin/koha/opac-detail.pl?biblionumber=<!-- TMPL_VAR NAME="biblionumber" -->">Close Shelf Browser</a></h5>
<table><tr>
use C4::Members;
use C4::VirtualShelves;
use C4::XSLT;
+use C4::ShelfBrowser;
BEGIN {
if (C4::Context->preference('BakerTaylorEnabled')) {
my $dbh = C4::Context->dbh;
my $marcflavour = C4::Context->preference("marcflavour");
my $marcnotesarray = GetMarcNotes ($record,$marcflavour);
-my $marcisbnsarray = GetMarcISBN ($record,$marcflavour);
my $marcauthorsarray = GetMarcAuthors ($record,$marcflavour);
my $marcsubjctsarray = GetMarcSubjects($record,$marcflavour);
my $marcseriesarray = GetMarcSeries ($record,$marcflavour);
MARCAUTHORS => $marcauthorsarray,
MARCSERIES => $marcseriesarray,
MARCURLS => $marcurlsarray,
- MARCISBNS => $marcisbnsarray,
norequests => $norequests,
RequestOnOpac => C4::Context->preference("RequestOnOpac"),
itemdata_ccode => $itemfields{ccode},
if (C4::Context->preference("OPACShelfBrowser")) {
# pick the first itemnumber unless one was selected by the user
my $starting_itemnumber = $query->param('shelfbrowse_itemnumber'); # || $items[0]->{itemnumber};
- $template->param( OpenOPACShelfBrowser => 1) if $starting_itemnumber;
- # find the right cn_sort value for this item
- my ($starting_cn_sort, $starting_homebranch, $starting_location);
- my $sth_get_cn_sort = $dbh->prepare("SELECT cn_sort,homebranch,location from items where itemnumber=?");
- $sth_get_cn_sort->execute($starting_itemnumber);
- while (my $result = $sth_get_cn_sort->fetchrow_hashref()) {
- $starting_cn_sort = $result->{'cn_sort'};
- $starting_homebranch->{code} = $result->{'homebranch'};
- $starting_homebranch->{description} = $branches->{$result->{'homebranch'}}{branchname};
- $starting_location->{code} = $result->{'location'};
- $starting_location->{description} = GetAuthorisedValueDesc('','', $result->{'location'} ,'','','LOC', 'opac');
-
- }
-
- ## List of Previous Items
- # order by cn_sort, which should include everything we need for ordering purposes (though not
- # for limits, those need to be handled separately
- my $sth_shelfbrowse_previous;
- if (defined $starting_location->{code}) {
- $sth_shelfbrowse_previous = $dbh->prepare("
- SELECT *
- FROM items
- WHERE
- ((cn_sort = ? AND itemnumber < ?) OR cn_sort < ?) AND
- homebranch = ? AND location = ?
- ORDER BY cn_sort DESC, itemnumber LIMIT 3
- ");
- $sth_shelfbrowse_previous->execute($starting_cn_sort, $starting_itemnumber, $starting_cn_sort, $starting_homebranch->{code}, $starting_location->{code});
- } else {
- $sth_shelfbrowse_previous = $dbh->prepare("
- SELECT *
- FROM items
- WHERE
- ((cn_sort = ? AND itemnumber < ?) OR cn_sort < ?) AND
- homebranch = ?
- ORDER BY cn_sort DESC, itemnumber LIMIT 3
- ");
- $sth_shelfbrowse_previous->execute($starting_cn_sort, $starting_itemnumber, $starting_cn_sort, $starting_homebranch->{code});
- }
- my @previous_items;
- while (my $this_item = $sth_shelfbrowse_previous->fetchrow_hashref()) {
- my $sth_get_biblio = $dbh->prepare("SELECT biblio.*,biblioitems.isbn AS isbn FROM biblio LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber WHERE biblio.biblionumber=?");
- $sth_get_biblio->execute($this_item->{biblionumber});
- while (my $this_biblio = $sth_get_biblio->fetchrow_hashref()) {
- $this_item->{'title'} = $this_biblio->{'title'};
- 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);
- }
- unshift @previous_items, $this_item;
- }
-
- ## List of Next Items; this also intentionally catches the current item
- my $sth_shelfbrowse_next;
- if (defined $starting_location->{code}) {
- $sth_shelfbrowse_next = $dbh->prepare("
- SELECT *
- FROM items
- WHERE
- ((cn_sort = ? AND itemnumber >= ?) OR cn_sort > ?) AND
- homebranch = ? AND location = ?
- ORDER BY cn_sort, itemnumber LIMIT 3
- ");
- $sth_shelfbrowse_next->execute($starting_cn_sort, $starting_itemnumber, $starting_cn_sort, $starting_homebranch->{code}, $starting_location->{code});
- } else {
- $sth_shelfbrowse_next = $dbh->prepare("
- SELECT *
- FROM items
- WHERE
- ((cn_sort = ? AND itemnumber >= ?) OR cn_sort > ?) AND
- homebranch = ?
- ORDER BY cn_sort, itemnumber LIMIT 3
- ");
- $sth_shelfbrowse_next->execute($starting_cn_sort, $starting_itemnumber, $starting_cn_sort, $starting_homebranch->{code});
- }
- my @next_items;
- while (my $this_item = $sth_shelfbrowse_next->fetchrow_hashref()) {
- my $sth_get_biblio = $dbh->prepare("SELECT biblio.*,biblioitems.isbn AS isbn FROM biblio LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber WHERE biblio.biblionumber=?");
- $sth_get_biblio->execute($this_item->{biblionumber});
- while (my $this_biblio = $sth_get_biblio->fetchrow_hashref()) {
- $this_item->{'title'} = $this_biblio->{'title'};
- 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 @next_items, $this_item;
+ if (defined($starting_itemnumber)) {
+ $template->param( OpenOPACShelfBrowser => 1) if $starting_itemnumber;
+ my $nearby = GetNearbyItems($starting_itemnumber,3);
+
+ $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},
+ );
}
-
- # alas, these won't auto-vivify, see http://www.perlmonks.org/?node_id=508481
- my $shelfbrowser_next_itemnumber = $next_items[-1]->{itemnumber} if @next_items;
- my $shelfbrowser_next_biblionumber = $next_items[-1]->{biblionumber} if @next_items;
-
- $template->param(
- starting_homebranch => $starting_homebranch->{description},
- starting_location => $starting_location->{description},
- starting_itemnumber => $starting_itemnumber,
- shelfbrowser_prev_itemnumber => (@previous_items ? $previous_items[0]->{itemnumber} : 0),
- shelfbrowser_next_itemnumber => $shelfbrowser_next_itemnumber,
- shelfbrowser_prev_biblionumber => (@previous_items ? $previous_items[0]->{biblionumber} : 0),
- shelfbrowser_next_biblionumber => $shelfbrowser_next_biblionumber,
- PREVIOUS_SHELF_BROWSE => \@previous_items,
- NEXT_SHELF_BROWSE => \@next_items,
- );
}
if (C4::Context->preference("BakerTaylorEnabled")) {