Bug 6875 de-nesting C4::Items
[koha.git] / C4 / VirtualShelves.pm
blobc74ecb3ef871473908658ec1dda0b70b48687973
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::Debug;
31 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
33 BEGIN {
34 # set the version for version checking
35 $VERSION = 3.02;
36 require Exporter;
37 @ISA = qw(Exporter);
38 @EXPORT = qw(
39 &GetShelves &GetShelfContents &GetShelf
40 &AddToShelf &AddShelf
41 &ModShelf
42 &ShelfPossibleAction
43 &DelFromShelf &DelShelf
44 &GetBibliosShelves
46 @EXPORT_OK = qw(
47 &GetShelvesSummary &GetRecentShelves &GetAllShelves
48 &RefreshShelvesSummary &SetShelvesLimit
53 my $dbh = C4::Context->dbh;
55 =head1 NAME
57 C4::VirtualShelves - Functions for manipulating Koha virtual virtualshelves
59 =head1 SYNOPSIS
61 use C4::VirtualShelves;
63 =head1 DESCRIPTION
65 This module provides functions for manipulating virtual virtualshelves,
66 including creating and deleting virtualshelves, and adding and removing
67 bibs to and from virtualshelves.
69 =head1 FUNCTIONS
71 =head2 GetShelves
73 ($shelflist, $totshelves) = &GetShelves($mincategory, $row_count, $offset, $owner);
74 ($shelfnumber, $shelfhash) = each %{$shelflist};
76 Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
77 number of shelves that meet the C<$owner> and C<$mincategory> criteria. C<$mincategory>,
78 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
79 When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
80 C<$shelflist>is a reference-to-hash. The keys are the virtualshelves numbers (C<$shelfnumber>, above),
81 and the values (C<$shelfhash>, above) are themselves references-to-hash, with the following keys:
83 =over
85 =item C<$shelfhash-E<gt>{shelfname}>
87 A string. The name of the shelf.
89 =item C<$shelfhash-E<gt>{count}>
91 The number of virtuals on that virtualshelves.
93 =back
95 =cut
97 sub GetShelves ($$$$) {
98 my ($mincategory, $row_count, $offset, $owner) = @_;
99 my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
100 my @params1 = ($owner, $mincategory);
101 if ($mincategory > 1) {
102 shift @params;
103 shift @params1;
105 my $total = _shelf_count($owner, $mincategory);
106 # grab only the shelves meeting the row_count/offset spec...
107 my $query = qq(
108 SELECT virtualshelves.shelfnumber, virtualshelves.shelfname,owner,surname,firstname,virtualshelves.category,virtualshelves.sortfield,
109 count(virtualshelfcontents.biblionumber) as count
110 FROM virtualshelves
111 LEFT JOIN virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
112 LEFT JOIN borrowers ON virtualshelves.owner = borrowers.borrowernumber );
113 $query .= ($mincategory == 1) ? "WHERE owner=? AND category=?" : "WHERE category>=?";
114 $query .= qq(
115 GROUP BY virtualshelves.shelfnumber
116 ORDER BY virtualshelves.shelfname
117 LIMIT ?, ?);
118 my $sth2 = $dbh->prepare($query);
119 $sth2->execute(@params);
120 my %shelflist;
121 while ( my ( $shelfnumber, $shelfname, $owner, $surname,
122 $firstname, $category, $sortfield, $count ) = $sth2->fetchrow ) {
123 $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
124 $shelflist{$shelfnumber}->{'count'} = $count;
125 if($count eq 1){ $shelflist{$shelfnumber}->{'single'} = 1; }
126 $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
127 $shelflist{$shelfnumber}->{'category'} = $category;
128 $shelflist{$shelfnumber}->{'owner'} = $owner;
129 $shelflist{$shelfnumber}->{'surname'} = $surname;
130 $shelflist{$shelfnumber}->{'firstname'} = $firstname;
132 return ( \%shelflist, $total );
135 =head2 GetShelvesSummary
137 ($shelves, $total) = GetShelvesSummary($mincategory, $row_count, $offset, $owner)
139 Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
140 number of shelves that meet the C<$owner> and/or C<$mincategory> criteria. C<$mincategory>,
141 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
142 When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
144 =cut
146 sub GetShelvesSummary ($$$$) {
147 my ($mincategory, $row_count, $offset, $owner) = @_;
148 my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
149 my @params1 = ($owner, $mincategory);
150 if ($mincategory > 1) {
151 shift @params;
152 shift @params1;
154 my $total = _shelf_count($owner, $mincategory);
155 # grab only the shelves meeting the row_count/offset spec...
156 my $query = qq(
157 SELECT
158 virtualshelves.shelfnumber,
159 virtualshelves.shelfname,
160 owner,
161 CONCAT(firstname, ' ', surname) AS name,
162 virtualshelves.category,
163 count(virtualshelfcontents.biblionumber) AS count
164 FROM virtualshelves
165 LEFT JOIN virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
166 LEFT JOIN borrowers ON virtualshelves.owner = borrowers.borrowernumber );
167 $query .= ($mincategory == 1) ? "WHERE owner=? AND category=?" : "WHERE category>=?";
168 $query .= qq(
169 GROUP BY virtualshelves.shelfnumber
170 ORDER BY virtualshelves.category
171 DESC
172 LIMIT ?, ?);
173 my $sth2 = $dbh->prepare($query);
174 $sth2->execute(@params);
175 my $shelves = $sth2->fetchall_arrayref({});
176 return ($shelves, $total);
178 # Probably NOT the final implementation since it is still bulky (repeated hash keys).
179 # might like an array of rows of delimited values:
180 # 1|2||0|blacklist|112
181 # 2|6|Josh Ferraro|51|en_fuego|106
184 =head2 GetRecentShelves
186 ($shelflist, $total) = GetRecentShelves(1, $limit, $owner)
188 This function returns a reference to an array of hashrefs containing specified shelves sorted
189 by the date the shelf was last modified in descending order limited to the number of records
190 specified by C<$row_count>. If calling with C<$mincategory> other than 1, use undef as C<$owner>.
192 This function is intended to return a dataset reflecting the most recently active shelves for
193 the submitted parameters.
195 =cut
197 sub GetRecentShelves {
198 my ($mincategory, $row_count, $owner) = @_;
199 my $total = _shelf_count($owner, $mincategory);
200 my @params;
201 my $selection;
202 if (defined $owner) {
203 @params = ($owner, $mincategory);
204 $selection = ' WHERE owner = ? AND category = ?';
205 } else {
206 @params = ( $mincategory);
207 $selection = ' WHERE category >= ? ';
209 my $query = 'SELECT * FROM virtualshelves';
210 $query .= $selection;
211 $query .= ' ORDER BY lastmodified DESC';
212 if ($row_count){
213 $query .= ' LIMIT ?';
214 push @params,$row_count;
216 my $sth = $dbh->prepare($query);
217 $sth->execute(@params);
218 my $shelflist = $sth->fetchall_arrayref({});
219 return ( $shelflist, $total );
222 =head2 GetAllShelves
224 $shelflist = GetAllShelves($owner)
226 This function returns a reference to an array of hashrefs containing all shelves sorted
227 by the shelf name.
229 This function is intended to return a dataset reflecting all the shelves for
230 the submitted parameters.
232 =cut
234 sub GetAllShelves {
235 my ($category,$owner) = @_;
236 my $query = 'SELECT * FROM virtualshelves WHERE category = ? AND owner = ? ORDER BY shelfname ASC';
237 my $sth = $dbh->prepare( $query );
238 $sth->execute( $category, $owner );
239 return $sth->fetchall_arrayref({});
242 =head2 GetShelf
244 (shelfnumber,shelfname,owner,category,sortfield) = &GetShelf($shelfnumber);
246 Looks up information about the contents of virtual virtualshelves number
247 C<$shelfnumber>
249 Returns the database's information on 'virtualshelves' table.
251 =cut
253 sub GetShelf ($) {
254 my ($shelfnumber) = @_;
255 my $query = qq(
256 SELECT shelfnumber, shelfname, owner, category, sortfield
257 FROM virtualshelves
258 WHERE shelfnumber=?
260 my $sth = $dbh->prepare($query);
261 $sth->execute($shelfnumber);
262 return $sth->fetchrow;
265 =head2 GetShelfContents
267 $biblist = &GetShelfContents($shelfnumber);
269 Looks up information about the contents of virtual virtualshelves number
270 C<$shelfnumber>. Sorted by a field in the biblio table. copyrightdate
271 gives a desc sort.
273 Returns a reference-to-array, whose elements are references-to-hash,
274 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
276 Note: the notforloan status comes from the itemtype, and where it equals 0
277 it does not ensure that related items.notforloan status is likewise 0. The
278 caller has to check any items on their own, possibly with CanBookBeIssued
279 from C4::Circulation.
281 =cut
283 sub GetShelfContents ($;$$$) {
284 my ($shelfnumber, $row_count, $offset, $sortfield) = @_;
285 my $dbh=C4::Context->dbh();
286 my $sth1 = $dbh->prepare("SELECT count(*) FROM virtualshelfcontents WHERE shelfnumber = ?");
287 $sth1->execute($shelfnumber);
288 my $total = $sth1->fetchrow;
289 if(!$sortfield) {
290 my $sth2 = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
291 $sth2->execute($shelfnumber);
292 ($sortfield) = $sth2->fetchrow_array;
294 my $query =
295 " SELECT vc.biblionumber, vc.shelfnumber, vc.dateadded, itemtypes.*,
296 biblio.*, biblioitems.itemtype, biblioitems.publicationyear as year, biblioitems.publishercode, biblioitems.place, biblioitems.size, biblioitems.pages
297 FROM virtualshelfcontents vc
298 LEFT JOIN biblio ON vc.biblionumber = biblio.biblionumber
299 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
300 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
301 WHERE vc.shelfnumber=? ";
302 my @params = ($shelfnumber);
303 if($sortfield) {
304 $query .= " ORDER BY " . $sortfield;
305 $query .= " DESC " if ($sortfield eq 'copyrightdate');
307 if($row_count){
308 $query .= " LIMIT ?, ? ";
309 push (@params, ($offset ? $offset : 0));
310 push (@params, $row_count);
312 my $sth3 = $dbh->prepare($query);
313 $sth3->execute(@params);
314 return ($sth3->fetchall_arrayref({}), $total);
315 # Like the perldoc says,
316 # returns reference-to-array, where each element is reference-to-hash of the row:
317 # like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ]
318 # Suitable for use in TMPL_LOOP.
319 # See http://search.cpan.org/~timb/DBI-1.601/DBI.pm#fetchall_arrayref
320 # or newer, for your version of DBI.
323 =head2 AddShelf
325 $shelfnumber = &AddShelf( $shelfname, $owner, $category);
327 Creates a new virtual virtualshelves with name C<$shelfname>, owner C<$owner> and category
328 C<$category>.
330 Returns a code to know what's happen.
331 * -1 : if this virtualshelves already exist.
332 * $shelfnumber : if success.
334 =cut
336 sub AddShelf {
337 my ( $shelfname, $owner, $category, $sortfield ) = @_;
338 my $query = qq(
339 SELECT *
340 FROM virtualshelves
341 WHERE shelfname=? AND owner=?
343 my $sth = $dbh->prepare($query);
344 $sth->execute($shelfname,$owner);
345 ( $sth->rows ) and return (-1);
346 $query = qq(
347 INSERT INTO virtualshelves
348 (shelfname,owner,category,sortfield)
349 VALUES (?,?,?,?)
351 $sth = $dbh->prepare($query);
352 $sth->execute( $shelfname, $owner, $category, $sortfield );
353 my $shelfnumber = $dbh->{'mysql_insertid'};
354 return ($shelfnumber);
357 =head2 AddToShelf
359 &AddToShelf($biblionumber, $shelfnumber);
361 Adds bib number C<$biblionumber> to virtual virtualshelves number
362 C<$shelfnumber>, unless that bib is already on that shelf.
364 =cut
367 sub AddToShelf {
368 my ( $biblionumber, $shelfnumber ) = @_;
369 return unless $biblionumber;
370 my $query = qq(
371 SELECT *
372 FROM virtualshelfcontents
373 WHERE shelfnumber=? AND biblionumber=?
375 my $sth = $dbh->prepare($query);
377 $sth->execute( $shelfnumber, $biblionumber );
378 ($sth->rows) and return undef; # already on shelf
379 $query = qq(
380 INSERT INTO virtualshelfcontents
381 (shelfnumber, biblionumber, flags)
382 VALUES
383 (?, ?, 0)
385 $sth = $dbh->prepare($query);
386 $sth->execute( $shelfnumber, $biblionumber );
387 $query = qq(UPDATE virtualshelves
388 SET lastmodified = CURRENT_TIMESTAMP
389 WHERE shelfnumber = ?);
390 $sth = $dbh->prepare($query);
391 $sth->execute( $shelfnumber );
394 =head2 ModShelf
396 ModShelf($shelfnumber, $hashref)
398 Where $hashref->{column} = param
400 Modify the value into virtualshelves table with values given
401 from hashref, which each key of the hashref should be
402 the name of a column of virtualshelves.
404 =cut
406 sub ModShelf {
407 my $shelfnumber = shift;
408 my $shelf = shift;
410 if (exists $shelf->{shelfnumber}) {
411 carp "Should not use ModShelf to change shelfnumber";
412 return;
414 unless (defined $shelfnumber and $shelfnumber =~ /^\d+$/) {
415 carp "Invalid shelfnumber passed to ModShelf: $shelfnumber";
416 return;
419 my $query = "UPDATE virtualshelves SET ";
420 my @bind_params = ();
421 my @set_clauses = ();
423 foreach my $column (keys %$shelf) {
424 push @set_clauses, "$column = ?";
425 push @bind_params, $shelf->{$column};
428 if ($#set_clauses == -1) {
429 carp "No columns to update passed to ModShelf";
430 return;
432 $query .= join(", ", @set_clauses);
434 $query .= " WHERE shelfnumber = ? ";
435 push @bind_params, $shelfnumber;
437 $debug and warn "ModShelf query:\n $query\n",
438 "ModShelf query args: ", join(',', @bind_params), "\n";
439 my $sth = $dbh->prepare($query);
440 $sth->execute( @bind_params );
443 =head2 ShelfPossibleAction
445 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
447 C<$loggedinuser,$shelfnumber,$action>
449 $action can be "view" or "manage".
451 Returns 1 if the user can do the $action in the $shelfnumber shelf.
452 Returns 0 otherwise.
454 =cut
456 sub ShelfPossibleAction {
457 my ( $user, $shelfnumber, $action ) = @_;
458 my $query = qq(
459 SELECT owner,category
460 FROM virtualshelves
461 WHERE shelfnumber=?
463 my $sth = $dbh->prepare($query);
464 $sth->execute($shelfnumber);
465 my ( $owner, $category ) = $sth->fetchrow;
466 require C4::Members;
467 my $borrower = C4::Members::GetMemberDetails($user);
468 return 0 if not defined($user);
469 return 1 if ( $category >= 3); # open list
470 return 1 if (($category >= 2) and
471 defined($action) and $action eq 'view'); # public list, anybody can view
472 return 1 if (($category >= 2) and defined($user) and ($borrower->{authflags}->{superlibrarian} || $user == 0)); # public list, superlibrarian can edit/delete
473 return 1 if (defined($user) and $owner eq $user ); # user owns this list. Check last.
474 return 0;
477 =head2 DelFromShelf
479 &DelFromShelf( $biblionumber, $shelfnumber);
481 Removes bib number C<$biblionumber> from virtual virtualshelves number
482 C<$shelfnumber>. If the bib wasn't on that virtualshelves to begin with,
483 nothing happens.
485 =cut
488 sub DelFromShelf {
489 my ( $biblionumber, $shelfnumber ) = @_;
490 my $query = qq(
491 DELETE FROM virtualshelfcontents
492 WHERE shelfnumber=? AND biblionumber=?
494 my $sth = $dbh->prepare($query);
495 $sth->execute( $shelfnumber, $biblionumber );
498 =head2 DelShelf (old version)
500 ($status, $msg) = &DelShelf($shelfnumber);
502 Deletes virtual virtualshelves number C<$shelfnumber>. The virtualshelves must
503 be empty.
505 Returns a two-element array, where C<$status> is 0 if the operation
506 was successful, or non-zero otherwise. C<$msg> is "Done" in case of
507 success, or an error message giving the reason for failure.
509 =head2 DelShelf (current version)
511 $Number = DelShelf($shelfnumber);
513 This function deletes the shelf number, and all of it's content.
515 =cut
517 sub DelShelf {
518 unless (@_) {
519 carp "DelShelf called without valid argument (shelfnumber)";
520 return undef;
522 my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?");
523 return $sth->execute(shift);
526 =head2 GetBibShelves
528 This finds all the public lists that this bib record is in.
530 =cut
532 sub GetBibliosShelves {
533 my ( $biblionumber ) = @_;
534 my $dbh = C4::Context->dbh;
535 my $sth = $dbh->prepare('
536 SELECT vs.shelfname, vs.shelfnumber
537 FROM virtualshelves vs
538 JOIN virtualshelfcontents vc ON (vs.shelfnumber= vc.shelfnumber)
539 WHERE vs.category != 1
540 AND vc.biblionumber= ?
542 $sth->execute( $biblionumber );
543 return $sth->fetchall_arrayref({});
546 =head2 RefreshShelvesSummary
548 ($total, $pubshelves, $barshelves) = RefreshShelvesSummary($sessionID, $loggedinuser, $row_count);
550 Updates the current session and userenv with the most recent shelves
552 Returns the total number of shelves stored in the session/userenv along with two references each to an
553 array of hashes, one containing the C<$loggedinuser>'s private shelves and one containing all public/open shelves.
555 This function is used in conjunction with the 'Lists' button in masthead.inc.
557 =cut
559 sub RefreshShelvesSummary ($$$) {
560 require C4::Auth;
561 my ($sessionID, $loggedinuser, $row_count) = @_;
562 my $session = C4::Auth::get_session($sessionID);
563 my ($total, $totshelves, $barshelves, $pubshelves);
565 ($barshelves, $totshelves) = GetRecentShelves(1, $row_count, $loggedinuser);
566 $total->{'bartotal'} = $totshelves;
567 ($pubshelves, $totshelves) = GetRecentShelves(2, $row_count, undef);
568 $total->{'pubtotal'} = $totshelves;
570 # Update the current session with the latest shelves...
571 $session->param('barshelves', $barshelves);
572 $session->param('pubshelves', $pubshelves);
573 $session->param('totshelves', $total);
575 # likewise the userenv...
576 C4::Context->set_shelves_userenv('bar',$barshelves);
577 C4::Context->set_shelves_userenv('pub',$pubshelves);
578 C4::Context::set_shelves_userenv('tot',$total);
580 return ($total, $pubshelves, $barshelves);
583 # internal subs
585 sub _shelf_count ($$) {
586 my (@params) = @_;
587 # Find out how many shelves total meet the submitted criteria...
588 my $query = "SELECT count(*) FROM virtualshelves";
589 $query .= ($params[1] > 1) ? " WHERE category >= ?" : " WHERE owner=? AND category=?";
590 shift @params if $params[1] > 1;
591 my $sth = $dbh->prepare($query);
592 $sth->execute(@params);
593 my $total = $sth->fetchrow;
594 return $total;
597 sub _biblionumber_sth {
598 my ($shelf) = @_;
599 my $query = 'select biblionumber from virtualshelfcontents where shelfnumber = ?';
600 my $dbh = C4::Context->dbh;
601 my $sth = $dbh->prepare($query)
602 or die $dbh->errstr;
603 $sth->execute( $shelf )
604 or die $sth->errstr;
605 $sth;
608 sub each_biblionumbers (&$) {
609 my ($code,$shelf) = @_;
610 my $ref = _biblionumber_sth($shelf)->fetchall_arrayref;
611 map {
612 $_=$$_[0];
613 $code->();
614 } @$ref;
619 __END__
621 =head1 AUTHOR
623 Koha Development Team <http://koha-community.org/>
625 =head1 SEE ALSO
627 C4::Circulation::Circ2(3)
629 =cut