bug 4802 add missing order search help file
[koha.git] / C4 / VirtualShelves.pm
blobfc11fc903bb1ef076054fd8477e5586385cff529
1 # -*- tab-width: 8 -*-
2 # Please use 8-character tabs for this file (indents are every 4 characters)
4 package C4::VirtualShelves;
7 # Copyright 2000-2002 Katipo Communications
9 # This file is part of Koha.
11 # Koha is free software; you can redistribute it and/or modify it under the
12 # terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
16 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
17 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License along
21 # with Koha; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 use strict;
25 use warnings;
27 use Carp;
28 use C4::Context;
29 use C4::Circulation;
30 use C4::Debug;
31 use C4::Members;
32 require C4::Auth;
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
36 BEGIN {
37 # set the version for version checking
38 $VERSION = 3.02;
39 require Exporter;
40 @ISA = qw(Exporter);
41 @EXPORT = qw(
42 &GetShelves &GetShelfContents &GetShelf
43 &AddToShelf &AddToShelfFromBiblio &AddShelf
44 &ModShelf
45 &ShelfPossibleAction
46 &DelFromShelf &DelShelf
47 &GetBibliosShelves
49 @EXPORT_OK = qw(
50 &GetShelvesSummary &GetRecentShelves
51 &RefreshShelvesSummary &SetShelvesLimit
56 my $dbh = C4::Context->dbh;
58 =head1 NAME
60 C4::VirtualShelves - Functions for manipulating Koha virtual virtualshelves
62 =head1 SYNOPSIS
64 use C4::VirtualShelves;
66 =head1 DESCRIPTION
68 This module provides functions for manipulating virtual virtualshelves,
69 including creating and deleting virtualshelves, and adding and removing
70 items to and from virtualshelves.
72 =head1 FUNCTIONS
74 =over 2
76 =item GetShelves
78 ($shelflist, $totshelves) = &GetShelves($mincategory, $row_count, $offset, $owner);
79 ($shelfnumber, $shelfhash) = each %{$shelflist};
81 Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
82 number of shelves that meet the C<$owner> and C<$mincategory> criteria. C<$mincategory>,
83 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
84 When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
85 C<$shelflist>is a reference-to-hash. The keys are the virtualshelves numbers (C<$shelfnumber>, above),
86 and the values (C<$shelfhash>, above) are themselves references-to-hash, with the following keys:
88 =over 4
90 =item C<$shelfhash-E<gt>{shelfname}>
92 A string. The name of the shelf.
94 =item C<$shelfhash-E<gt>{count}>
96 The number of virtuals on that virtualshelves.
98 =back
100 =cut
102 sub GetShelves ($$$$) {
103 my ($mincategory, $row_count, $offset, $owner) = @_;
104 my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
105 my @params1 = ($owner, $mincategory);
106 if ($mincategory > 1) {
107 shift @params;
108 shift @params1;
110 my $total = _shelf_count($owner, $mincategory);
111 # grab only the shelves meeting the row_count/offset spec...
112 my $query = qq(
113 SELECT virtualshelves.shelfnumber, virtualshelves.shelfname,owner,surname,firstname,virtualshelves.category,virtualshelves.sortfield,
114 count(virtualshelfcontents.biblionumber) as count
115 FROM virtualshelves
116 LEFT JOIN virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
117 LEFT JOIN borrowers ON virtualshelves.owner = borrowers.borrowernumber );
118 $query .= ($mincategory == 1) ? "WHERE owner=? AND category=?" : "WHERE category>=?";
119 $query .= qq(
120 GROUP BY virtualshelves.shelfnumber
121 ORDER BY virtualshelves.category
122 DESC
123 LIMIT ?, ?);
124 my $sth2 = $dbh->prepare($query);
125 $sth2->execute(@params);
126 my %shelflist;
127 while ( my ( $shelfnumber, $shelfname, $owner, $surname,
128 $firstname, $category, $sortfield, $count ) = $sth2->fetchrow ) {
129 $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
130 $shelflist{$shelfnumber}->{'count'} = $count;
131 $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
132 $shelflist{$shelfnumber}->{'category'} = $category;
133 $shelflist{$shelfnumber}->{'owner'} = $owner;
134 $shelflist{$shelfnumber}->{'surname'} = $surname;
135 $shelflist{$shelfnumber}->{'firstname'} = $firstname;
137 return ( \%shelflist, $total );
140 =item GetShelvesSummary
142 ($shelves, $total) = GetShelvesSummary($mincategory, $row_count, $offset, $owner)
144 Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
145 number of shelves that meet the C<$owner> and/or C<$mincategory> criteria. C<$mincategory>,
146 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
147 When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
149 =cut
151 sub GetShelvesSummary ($$$$) {
152 my ($mincategory, $row_count, $offset, $owner) = @_;
153 my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
154 my @params1 = ($owner, $mincategory);
155 if ($mincategory > 1) {
156 shift @params;
157 shift @params1;
159 my $total = _shelf_count($owner, $mincategory);
160 # grab only the shelves meeting the row_count/offset spec...
161 my $query = qq(
162 SELECT
163 virtualshelves.shelfnumber,
164 virtualshelves.shelfname,
165 owner,
166 CONCAT(firstname, ' ', surname) AS name,
167 virtualshelves.category,
168 count(virtualshelfcontents.biblionumber) AS count
169 FROM virtualshelves
170 LEFT JOIN virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
171 LEFT JOIN borrowers ON virtualshelves.owner = borrowers.borrowernumber );
172 $query .= ($mincategory == 1) ? "WHERE owner=? AND category=?" : "WHERE category>=?";
173 $query .= qq(
174 GROUP BY virtualshelves.shelfnumber
175 ORDER BY virtualshelves.category
176 DESC
177 LIMIT ?, ?);
178 my $sth2 = $dbh->prepare($query);
179 $sth2->execute(@params);
180 my $shelves = $sth2->fetchall_arrayref({});
181 return ($shelves, $total);
183 # Probably NOT the final implementation since it is still bulky (repeated hash keys).
184 # might like an array of rows of delimited values:
185 # 1|2||0|blacklist|112
186 # 2|6|Josh Ferraro|51|en_fuego|106
189 =item GetRecentShelves
191 ($shelflist) = GetRecentShelves(1, $limit, $owner)
193 This function returns a references to an array of hashrefs containing specified shelves sorted
194 by the date the shelf was last modified in descending order limited to the number of records
195 specified by C<$row_count>. If calling with C<$mincategory> other than 1, use undef as C<$owner>.
197 This function is intended to return a dataset reflecting the most recently active shelves for
198 the submitted parameters.
200 =cut
202 sub GetRecentShelves ($$$) {
203 my ($mincategory, $row_count, $owner) = @_;
204 my (@shelflist);
205 my $total = _shelf_count($owner, $mincategory);
206 my @params = ($owner, $mincategory, 0, $row_count); #FIXME: offset is hardcoded here, but could be passed in for enhancements
207 shift @params if (not defined $owner);
208 my $query = "SELECT * FROM virtualshelves";
209 $query .= ((defined $owner) ? " WHERE owner = ? AND category = ?" : " WHERE category >= ? ");
210 $query .= " ORDER BY lastmodified DESC LIMIT ?, ?";
211 my $sth = $dbh->prepare($query);
212 $sth->execute(@params);
213 @shelflist = $sth->fetchall_arrayref({});
214 return ( \@shelflist, $total );
217 =item GetShelf
219 (shelfnumber,shelfname,owner,category,sortfield) = &GetShelf($shelfnumber);
221 Looks up information about the contents of virtual virtualshelves number
222 C<$shelfnumber>
224 Returns the database's information on 'virtualshelves' table.
226 =cut
228 sub GetShelf ($) {
229 my ($shelfnumber) = @_;
230 my $query = qq(
231 SELECT shelfnumber, shelfname, owner, category, sortfield
232 FROM virtualshelves
233 WHERE shelfnumber=?
235 my $sth = $dbh->prepare($query);
236 $sth->execute($shelfnumber);
237 return $sth->fetchrow;
240 =item GetShelfContents
242 $itemlist = &GetShelfContents($shelfnumber);
244 Looks up information about the contents of virtual virtualshelves number
245 C<$shelfnumber>. Sorted by a field in the biblio table. copyrightdate
246 gives a desc sort.
248 Returns a reference-to-array, whose elements are references-to-hash,
249 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
251 Note: the notforloan status comes from the itemtype, and where it equals 0
252 it does not ensure that related items.notforloan status is likewise 0. The
253 caller has to check any items on their own, possibly with CanBookBeIssued
254 from C4::Circulation.
256 =cut
258 sub GetShelfContents ($;$$$) {
259 my ($shelfnumber, $row_count, $offset, $sortfield) = @_;
260 my $dbh=C4::Context->dbh();
261 my $sth1 = $dbh->prepare("SELECT count(*) FROM virtualshelfcontents WHERE shelfnumber = ?");
262 $sth1->execute($shelfnumber);
263 my $total = $sth1->fetchrow;
264 if(!$sortfield) {
265 my $sth2 = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
266 $sth2->execute($shelfnumber);
267 ($sortfield) = $sth2->fetchrow_array;
269 my $query =
270 " SELECT vc.biblionumber, vc.shelfnumber, vc.dateadded, itemtypes.*,
271 biblio.*, biblioitems.itemtype, biblioitems.publicationyear
272 FROM virtualshelfcontents vc
273 LEFT JOIN biblio ON vc.biblionumber = biblio.biblionumber
274 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
275 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
276 WHERE vc.shelfnumber=? ";
277 my @params = ($shelfnumber);
278 if($sortfield) {
279 $query .= " ORDER BY " . $sortfield;
280 $query .= " DESC " if ($sortfield eq 'copyrightdate');
282 if($row_count){
283 $query .= " LIMIT ?, ? ";
284 push (@params, ($offset ? $offset : 0));
285 push (@params, $row_count);
287 my $sth3 = $dbh->prepare($query);
288 $sth3->execute(@params);
289 return ($sth3->fetchall_arrayref({}), $total);
290 # Like the perldoc says,
291 # returns reference-to-array, where each element is reference-to-hash of the row:
292 # like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ]
293 # Suitable for use in TMPL_LOOP.
294 # See http://search.cpan.org/~timb/DBI-1.601/DBI.pm#fetchall_arrayref
295 # or newer, for your version of DBI.
298 =item AddShelf
300 $shelfnumber = &AddShelf( $shelfname, $owner, $category);
302 Creates a new virtual virtualshelves with name C<$shelfname>, owner C<$owner> and category
303 C<$category>.
305 Returns a code to know what's happen.
306 * -1 : if this virtualshelves already exist.
307 * $shelfnumber : if success.
309 =cut
311 sub AddShelf {
312 my ( $shelfname, $owner, $category, $sortfield ) = @_;
313 my $query = qq(
314 SELECT *
315 FROM virtualshelves
316 WHERE shelfname=? AND owner=?
318 my $sth = $dbh->prepare($query);
319 $sth->execute($shelfname,$owner);
320 ( $sth->rows ) and return (-1);
321 $query = qq(
322 INSERT INTO virtualshelves
323 (shelfname,owner,category,sortfield)
324 VALUES (?,?,?,?)
326 $sth = $dbh->prepare($query);
327 $sth->execute( $shelfname, $owner, $category, $sortfield );
328 my $shelfnumber = $dbh->{'mysql_insertid'};
329 return ($shelfnumber);
332 =item AddToShelf
334 &AddToShelf($biblionumber, $shelfnumber);
336 Adds item number C<$biblionumber> to virtual virtualshelves number
337 C<$shelfnumber>, unless that item is already on that shelf.
339 =cut
342 sub AddToShelf {
343 my ( $biblionumber, $shelfnumber ) = @_;
344 return unless $biblionumber;
345 my $query = qq(
346 SELECT *
347 FROM virtualshelfcontents
348 WHERE shelfnumber=? AND biblionumber=?
350 my $sth = $dbh->prepare($query);
352 $sth->execute( $shelfnumber, $biblionumber );
353 ($sth->rows) and return undef; # already on shelf
354 $query = qq(
355 INSERT INTO virtualshelfcontents
356 (shelfnumber, biblionumber, flags)
357 VALUES
358 (?, ?, 0)
360 $sth = $dbh->prepare($query);
361 $sth->execute( $shelfnumber, $biblionumber );
362 $query = qq(UPDATE virtualshelves
363 SET lastmodified = CURRENT_TIMESTAMP
364 WHERE shelfnumber = ?);
365 $sth = $dbh->prepare($query);
366 $sth->execute( $shelfnumber );
369 =item AddToShelfFromBiblio
371 &AddToShelfFromBiblio($biblionumber, $shelfnumber)
373 this function allow to add a virtual into the shelf number $shelfnumber
374 from biblionumber.
376 =cut
378 sub AddToShelfFromBiblio {
379 my ( $biblionumber, $shelfnumber ) = @_;
380 return unless $biblionumber;
381 my $query = qq(
382 SELECT *
383 FROM virtualshelfcontents
384 WHERE shelfnumber=? AND biblionumber=?
386 my $sth = $dbh->prepare($query);
387 $sth->execute( $shelfnumber, $biblionumber );
388 unless ( $sth->rows ) {
389 my $query =qq(
390 INSERT INTO virtualshelfcontents
391 (shelfnumber, biblionumber, flags)
392 VALUES
393 (?, ?, 0)
395 $sth = $dbh->prepare($query);
396 $sth->execute( $shelfnumber, $biblionumber );
397 $query = qq(UPDATE virtualshelves
398 SET lastmodified = CURRENT_TIMESTAMP
399 WHERE shelfnumber = ?);
400 $sth = $dbh->prepare($query);
401 $sth->execute( $shelfnumber );
405 =item ModShelf
407 ModShelf($shelfnumber, $hashref)
409 Where $hashref->{column} = param
411 Modify the value into virtualshelves table with values given
412 from hashref, which each key of the hashref should be
413 the name of a column of virtualshelves.
415 =cut
417 sub ModShelf {
418 my $shelfnumber = shift;
419 my $shelf = shift;
421 if (exists $shelf->{shelfnumber}) {
422 carp "Should not use ModShelf to change shelfnumber";
423 return;
425 unless (defined $shelfnumber and $shelfnumber =~ /^\d+$/) {
426 carp "Invalid shelfnumber passed to ModShelf: $shelfnumber";
427 return;
430 my $query = "UPDATE virtualshelves SET ";
431 my @bind_params = ();
432 my @set_clauses = ();
434 foreach my $column (keys %$shelf) {
435 push @set_clauses, "$column = ?";
436 push @bind_params, $shelf->{$column};
439 if ($#set_clauses == -1) {
440 carp "No columns to update passed to ModShelf";
441 return;
443 $query .= join(", ", @set_clauses);
445 $query .= " WHERE shelfnumber = ? ";
446 push @bind_params, $shelfnumber;
448 $debug and warn "ModShelf query:\n $query\n",
449 "ModShelf query args: ", join(',', @bind_params), "\n";
450 my $sth = $dbh->prepare($query);
451 $sth->execute( @bind_params );
454 =item ShelfPossibleAction
456 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
458 C<$loggedinuser,$shelfnumber,$action>
460 $action can be "view" or "manage".
462 Returns 1 if the user can do the $action in the $shelfnumber shelf.
463 Returns 0 otherwise.
465 =cut
467 sub ShelfPossibleAction {
468 my ( $user, $shelfnumber, $action ) = @_;
469 my $query = qq(
470 SELECT owner,category
471 FROM virtualshelves
472 WHERE shelfnumber=?
474 my $sth = $dbh->prepare($query);
475 $sth->execute($shelfnumber);
476 my ( $owner, $category ) = $sth->fetchrow;
477 my $borrower = GetMemberDetails($user);
478 return 0 if not defined($user);
479 return 1 if ( $category >= 3); # open list
480 return 1 if (($category >= 2) and
481 defined($action) and $action eq 'view'); # public list, anybody can view
482 return 1 if (($category >= 2) and defined($user) and ($borrower->{authflags}->{superlibrarian} || $user == 0)); # public list, superlibrarian can edit/delete
483 return 1 if (defined($user) and $owner eq $user ); # user owns this list. Check last.
484 return 0;
487 =item DelFromShelf
489 &DelFromShelf( $biblionumber, $shelfnumber);
491 Removes item number C<$biblionumber> from virtual virtualshelves number
492 C<$shelfnumber>. If the item wasn't on that virtualshelves to begin with,
493 nothing happens.
495 =cut
498 sub DelFromShelf {
499 my ( $biblionumber, $shelfnumber ) = @_;
500 my $query = qq(
501 DELETE FROM virtualshelfcontents
502 WHERE shelfnumber=? AND biblionumber=?
504 my $sth = $dbh->prepare($query);
505 $sth->execute( $shelfnumber, $biblionumber );
508 =item DelShelf (old version)
510 ($status, $msg) = &DelShelf($shelfnumber);
512 Deletes virtual virtualshelves number C<$shelfnumber>. The virtualshelves must
513 be empty.
515 Returns a two-element array, where C<$status> is 0 if the operation
516 was successful, or non-zero otherwise. C<$msg> is "Done" in case of
517 success, or an error message giving the reason for failure.
519 =item DelShelf (current version)
521 $Number = DelShelf($shelfnumber);
523 This function deletes the shelf number, and all of it's content.
525 =cut
527 sub DelShelf {
528 unless (@_) {
529 carp "DelShelf called without valid argument (shelfnumber)";
530 return undef;
532 my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?");
533 return $sth->execute(shift);
536 =item GetBibShelves
538 This finds all the public lists that this bib record is in.
540 =cut
542 sub GetBibliosShelves {
543 my ( $biblionumber ) = @_;
544 my $dbh = C4::Context->dbh;
545 my $sth = $dbh->prepare('
546 SELECT vs.shelfname, vs.shelfnumber
547 FROM virtualshelves vs
548 JOIN virtualshelfcontents vc ON (vs.shelfnumber= vc.shelfnumber)
549 WHERE vs.category != 1
550 AND vc.biblionumber= ?
552 $sth->execute( $biblionumber );
553 return $sth->fetchall_arrayref({});
556 =item RefreshShelvesSummary
558 ($total, $pubshelves, $barshelves) = RefreshShelvesSummary($sessionID, $loggedinuser, $row_count);
560 Updates the current session and userenv with the most recent shelves
562 Returns the total number of shelves stored in the session/userenv along with two references each to an
563 array of hashes, one containing the C<$loggedinuser>'s private shelves and one containing all public/open shelves.
565 This function is used in conjunction with the 'Lists' button in masthead.inc.
567 =cut
569 sub RefreshShelvesSummary ($$$) {
571 my ($sessionID, $loggedinuser, $row_count) = @_;
572 my $session = C4::Auth::get_session($sessionID);
573 my ($total, $totshelves, $barshelves, $pubshelves);
575 ($barshelves, $totshelves) = GetRecentShelves(1, $row_count, $loggedinuser);
576 $total->{'bartotal'} = $totshelves;
577 ($pubshelves, $totshelves) = GetRecentShelves(2, $row_count, undef);
578 $total->{'pubtotal'} = $totshelves;
580 # Update the current session with the latest shelves...
581 $session->param('barshelves', $barshelves->[0]);
582 $session->param('pubshelves', $pubshelves->[0]);
583 $session->param('totshelves', $total);
585 # likewise the userenv...
586 C4::Context->set_shelves_userenv('bar',$barshelves->[0]);
587 C4::Context->set_shelves_userenv('pub',$pubshelves->[0]);
588 C4::Context::set_shelves_userenv('tot',$total);
590 return ($total, $pubshelves, $barshelves);
593 # internal subs
595 sub _shelf_count ($$) {
596 my (@params) = @_;
597 # Find out how many shelves total meet the submitted criteria...
598 my $query = "SELECT count(*) FROM virtualshelves";
599 $query .= ($params[1] > 1) ? " WHERE category >= ?" : " WHERE owner=? AND category=?";
600 shift @params if $params[1] > 1;
601 my $sth = $dbh->prepare($query);
602 $sth->execute(@params);
603 my $total = $sth->fetchrow;
604 return $total;
607 sub _biblionumber_sth {
608 my ($shelf) = @_;
609 my $query = 'select biblionumber from virtualshelfcontents where shelfnumber = ?';
610 my $dbh = C4::Context->dbh;
611 my $sth = $dbh->prepare($query)
612 or die $dbh->errstr;
613 $sth->execute( $shelf )
614 or die $sth->errstr;
615 $sth;
618 sub each_biblionumbers (&$) {
619 my ($code,$shelf) = @_;
620 my $ref = _biblionumber_sth($shelf)->fetchall_arrayref;
621 map {
622 $_=$$_[0];
623 $code->();
624 } @$ref;
629 __END__
631 =back
633 =head1 AUTHOR
635 Koha Developement team <info@koha.org>
637 =head1 SEE ALSO
639 C4::Circulation::Circ2(3)
641 =cut