bug 8215: (followup) make sure C4::CourseReserves doesn't export anything
[koha.git] / C4 / ShelfBrowser.pm
1 #!/usr/bin/perl
2
3 package C4::ShelfBrowser;
4
5 # Copyright 2010 Catalyst IT
6 #
7 # This file is part of Koha.
8 #
9 # Koha is free software; you can redistribute it and/or modify it under the
10 # terms of the GNU General Public License as published by the Free Software
11 # Foundation; either version 2 of the License, or (at your option) any later
12 # version.
13 #
14 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License along
19 # with Koha; if not, write to the Free Software Foundation, Inc.,
20 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22 use strict;
23 use warnings;
24
25 use C4::Biblio;
26 use C4::Branch;
27 use C4::Context;
28 use C4::Koha;
29
30 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
31
32 BEGIN {
33     $VERSION = 3.07.00.049;
34         require Exporter;
35         @ISA    = qw(Exporter);
36         @EXPORT = qw(
37             &GetNearbyItems
38     );
39     @EXPORT_OK = qw(
40     );
41 }
42
43 =head1 NAME
44
45 C4::ShelfBrowser - functions that deal with the shelf browser feature found in
46 the OPAC.
47
48 =head1 SYNOPSIS
49
50   use C4::ShelfBrowser;
51
52 =head1 DESCRIPTION
53
54 This module provides functions to get items nearby to another item, for use
55 in the shelf browser function.
56
57 'Nearby' is controlled by a handful of system preferences that specify what
58 to take into account.
59
60 =head1 FUNCTIONS
61
62 =head2 GetNearbyItems($itemnumber, [$num_each_side])
63
64   $nearby = GetNearbyItems($itemnumber, [$num_each_side]);
65
66   @next = @{ $nearby->{next} };
67   @prev = @{ $nearby->{prev} };
68
69   foreach (@next) {
70       # These won't format well like this, but here are the fields
71           print $_->{title};
72           print $_->{biblionumber};
73           print $_->{itemnumber};
74           print $_->{browser_normalized_upc};
75           print $_->{browser_normalized_oclc};
76           print $_->{browser_normalized_isbn};
77   }
78
79   # This is the information required to scroll the browser to the next left
80   # or right set. Can be derived from next/prev, but it's here for convenience.
81   print $nearby->{prev_itemnumber};
82   print $nearby->{next_itemnumber};
83   print $nearby->{prev_biblionumber};
84   print $nearby->{next_biblionumber};
85
86   # These will be undef if the values are not used to calculate the 
87   # nearby items.
88   print $nearby->{starting_homebranch}->{code};
89   print $nearby->{starting_homebranch}->{description};
90   print $nearby->{starting_location}->{code};
91   print $nearby->{starting_location}->{description};
92   print $nearby->{starting_ccode}->{code};
93   print $nearby->{starting_ccode}->{description};
94
95   print $nearby->{starting_itemnumber};
96   
97 This finds the items that are nearby to the supplied item, and supplies
98 those previous and next, along with the other useful information for displaying
99 the shelf browser.
100
101 It automatically applies the following user preferences to work out how to
102 calculate things: C<ShelfBrowserUsesLocation>, C<ShelfBrowserUsesHomeBranch>, 
103 C<ShelfBrowserUsesCcode>.
104
105 The option C<$num_each_side> value determines how many items will be fetched
106 each side of the supplied item. Note that the item itself is the first entry
107 in the 'next' set, and counts towards this limit (this is to keep the
108 behaviour consistant with the code that this is a refactor of.) Default is
109 3.
110
111 This will throw an exception if something went wrong.
112
113 =cut
114
115 sub GetNearbyItems {
116         my ($itemnumber, $num_each_side) = @_;
117         $num_each_side ||= 3;
118
119     my $dbh         = C4::Context->dbh;
120     my $marcflavour = C4::Context->preference("marcflavour");
121     my $branches = GetBranches();
122
123     my $sth_get_item_details = $dbh->prepare("SELECT cn_sort,homebranch,location,ccode from items where itemnumber=?");
124     $sth_get_item_details->execute($itemnumber);
125     my $item_details_result = $sth_get_item_details->fetchrow_hashref();
126     die "Unable to find item '$itemnumber' for shelf browser" if (!$sth_get_item_details);
127     my $start_cn_sort = $item_details_result->{'cn_sort'};
128
129     my ($start_homebranch, $start_location, $start_ccode);
130     if (C4::Context->preference('ShelfBrowserUsesHomeBranch') && 
131         defined($item_details_result->{'homebranch'})) {
132         $start_homebranch->{code} = $item_details_result->{'homebranch'};
133         $start_homebranch->{description} = $branches->{$item_details_result->{'homebranch'}}{branchname};
134     }
135     if (C4::Context->preference('ShelfBrowserUsesLocation') && 
136         defined($item_details_result->{'location'})) {
137         $start_location->{code} = $item_details_result->{'location'};
138         $start_location->{description} = GetAuthorisedValueDesc('','',$item_details_result->{'location'},'','','LOC','opac');
139     }
140     if (C4::Context->preference('ShelfBrowserUsesCcode') && 
141         defined($item_details_result->{'ccode'})) {
142         $start_ccode->{code} = $item_details_result->{'ccode'};
143         $start_ccode->{description} = GetAuthorisedValueDesc('', '', $item_details_result->{'ccode'}, '', '', 'CCODE', 'opac');
144     }
145
146     # Build the query for previous and next items
147     my $prev_query ='
148         SELECT *
149         FROM items
150         WHERE
151             ((cn_sort = ? AND itemnumber < ?) OR cn_sort < ?) ';
152     my $next_query ='
153         SELECT *
154         FROM items
155         WHERE
156             ((cn_sort = ? AND itemnumber >= ?) OR cn_sort > ?) ';
157     my @params;
158     my $query_cond;
159     push @params, ($start_cn_sort, $itemnumber, $start_cn_sort);
160     if ($start_homebranch) {
161         $query_cond .= 'AND homebranch = ? ';
162         push @params, $start_homebranch->{code};
163     }
164     if ($start_location) {
165         $query_cond .= 'AND location = ? ';
166         push @params, $start_location->{code};
167     }
168     if ($start_ccode) {
169         $query_cond .= 'AND ccode = ? ';
170         push @params, $start_ccode->{code};
171     }
172
173     my $sth_prev_items = $dbh->prepare($prev_query . $query_cond . ' ORDER BY cn_sort DESC, itemnumber LIMIT ?');
174     my $sth_next_items = $dbh->prepare($next_query . $query_cond . ' ORDER BY cn_sort, itemnumber LIMIT ?');
175     push @params, $num_each_side;
176     $sth_prev_items->execute(@params);
177     $sth_next_items->execute(@params);
178     
179     # Now we have the query run, suck out the data like marrow
180     my @prev_items = reverse GetShelfInfo($sth_prev_items, $marcflavour);
181     my @next_items = GetShelfInfo($sth_next_items, $marcflavour);
182
183     my (
184         $next_itemnumber, $next_biblionumber,
185         $prev_itemnumber, $prev_biblionumber
186     );
187
188     $next_itemnumber = $next_items[-1]->{itemnumber} if @next_items;
189     $next_biblionumber = $next_items[-1]->{biblionumber} if @next_items;
190
191     $prev_itemnumber = $prev_items[0]->{itemnumber} if @prev_items;
192     $prev_biblionumber = $prev_items[0]->{biblionumber} if @prev_items;
193
194     my %result = (
195         next                => \@next_items,
196         prev                => \@prev_items,
197         next_itemnumber     => $next_itemnumber,
198         next_biblionumber   => $next_biblionumber,
199         prev_itemnumber     => $prev_itemnumber,
200         prev_biblionumber   => $prev_biblionumber,   
201         starting_itemnumber => $itemnumber,
202     );
203     $result{starting_homebranch} = $start_homebranch if $start_homebranch;
204     $result{starting_location}   = $start_location   if $start_location;
205     $result{starting_ccode}         = $start_ccode      if $start_ccode;
206     return \%result;
207 }
208
209 # This runs through a statement handle and pulls out all the items in it, fills
210 # them up with additional info that shelves want, and returns those as a list.
211 # Not really intended to be exported.
212 sub GetShelfInfo {
213     my ($sth, $marcflavour) = @_;
214
215     my @items;
216     while (my $this_item = $sth->fetchrow_hashref()) {
217         my $this_biblio = GetBibData($this_item->{biblionumber});
218         next if (!defined($this_biblio));
219         $this_item->{'title'} = $this_biblio->{'title'};
220         my $this_record = GetMarcBiblio($this_biblio->{'biblionumber'});
221         $this_item->{'browser_normalized_upc'} = GetNormalizedUPC($this_record,$marcflavour);
222         $this_item->{'browser_normalized_oclc'} = GetNormalizedOCLCNumber($this_record,$marcflavour);
223         $this_item->{'browser_normalized_isbn'} = GetNormalizedISBN(undef,$this_record,$marcflavour);
224         push @items, $this_item;
225     }
226     return @items;
227 }
228
229 # Fetches some basic biblio data needed by the shelf stuff
230 sub GetBibData {
231         my ($bibnum) = @_;
232
233     my $dbh         = C4::Context->dbh;
234     my $sth = $dbh->prepare("SELECT biblionumber, title FROM biblio WHERE biblionumber=?");
235     $sth->execute($bibnum);
236     my $bib = $sth->fetchrow_hashref();
237     return $bib;
238 }
239
240 1;