kohabug 2392 Changing array dereferencing syntax
[koha.git] / C4 / VirtualShelves.pm
1 # -*- tab-width: 8 -*-
2 # Please use 8-character tabs for this file (indents are every 4 characters)
3
4 package C4::VirtualShelves;
5
6
7 # Copyright 2000-2002 Katipo Communications
8 #
9 # This file is part of Koha.
10 #
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.
15 #
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.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
22 # Suite 330, Boston, MA  02111-1307 USA
23
24 use strict;
25 use Carp;
26 use C4::Context;
27 use C4::Circulation;
28 use C4::Debug;
29 use C4::Members;
30
31 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
32
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                 &GetRecentShelves &GetShelvesSummary
41
42         &AddToShelf &AddToShelfFromBiblio &AddShelf
43
44                 &SetShelvesLimit
45                 &RefreshShelvesSummary
46
47         &ModShelf
48         &ShelfPossibleAction
49         &DelFromShelf &DelShelf
50         );
51 }
52
53 use C4::Auth qw(get_session);
54
55 my $dbh = C4::Context->dbh;
56
57 =head1 NAME
58
59 C4::VirtualShelves - Functions for manipulating Koha virtual virtualshelves
60
61 =head1 SYNOPSIS
62
63   use C4::VirtualShelves;
64
65 =head1 DESCRIPTION
66
67 This module provides functions for manipulating virtual virtualshelves,
68 including creating and deleting virtualshelves, and adding and removing
69 items to and from virtualshelves.
70
71 =head1 FUNCTIONS
72
73 =over 2
74
75 =item GetShelves
76
77   ($shelflist, $totshelves) = &GetShelves($mincategory, $row_count, $offset, $owner);
78   ($shelfnumber, $shelfhash) = each %{$shelflist};
79
80 Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
81 number of shelves that meet the C<$owner> and C<$mincategory> criteria.  C<$mincategory>,
82 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
83 When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
84 C<$shelflist>is a reference-to-hash. The keys are the virtualshelves numbers (C<$shelfnumber>, above),
85 and the values (C<$shelfhash>, above) are themselves references-to-hash, with the following keys:
86
87 =over 4
88
89 =item C<$shelfhash-E<gt>{shelfname}>
90
91 A string. The name of the shelf.
92
93 =item C<$shelfhash-E<gt>{count}>
94
95 The number of virtuals on that virtualshelves.
96
97 =back
98
99 =cut
100
101 sub GetShelves ($$$$) {
102     my ($mincategory, $row_count, $offset, $owner) = @_;
103         my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
104         my @params1 = ($owner, $mincategory);
105         if ($mincategory > 1) {
106                 shift @params;
107                 shift @params1;
108         }
109         my $total = _shelf_count($owner, $mincategory);
110     # grab only the shelves meeting the row_count/offset spec...
111     my $query = qq(
112         SELECT virtualshelves.shelfnumber, virtualshelves.shelfname,owner,surname,firstname,virtualshelves.category,virtualshelves.sortfield,
113                count(virtualshelfcontents.biblionumber) as count
114         FROM   virtualshelves
115             LEFT JOIN   virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
116             LEFT JOIN   borrowers ON virtualshelves.owner = borrowers.borrowernumber );
117     $query .= ($mincategory == 1) ? "WHERE  owner=? AND category=?" : "WHERE category>=?";
118         $query .= qq(
119         GROUP BY virtualshelves.shelfnumber
120         ORDER BY virtualshelves.category
121                 DESC 
122                 LIMIT ?, ?);
123     my $sth2 = $dbh->prepare($query);
124     $sth2->execute(@params);
125     my %shelflist;
126     while ( my ( $shelfnumber, $shelfname, $owner, $surname,
127                 $firstname,   $category,  $sortfield, $count ) = $sth2->fetchrow ) {
128         $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
129         $shelflist{$shelfnumber}->{'count'}     = $count;
130         $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
131         $shelflist{$shelfnumber}->{'category'}  = $category;
132         $shelflist{$shelfnumber}->{'owner'}     = $owner;
133         $shelflist{$shelfnumber}->{'surname'}   = $surname;
134         $shelflist{$shelfnumber}->{'firstname'} = $firstname;
135     }
136     return ( \%shelflist, $total );
137 }
138
139 =item GetShelvesSummary
140
141         ($shelves, $total) = GetShelvesSummary($mincategory, $row_count, $offset, $owner)
142
143 Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
144 number of shelves that meet the C<$owner> and/or C<$mincategory> criteria. C<$mincategory>,
145 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
146 When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
147
148 =cut
149
150 sub GetShelvesSummary ($$$$) {
151     my ($mincategory, $row_count, $offset, $owner) = @_;
152         my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
153         my @params1 = ($owner, $mincategory);
154         if ($mincategory > 1) {
155                 shift @params;
156                 shift @params1;
157         }
158         my $total = _shelf_count($owner, $mincategory);
159     # grab only the shelves meeting the row_count/offset spec...
160         my $query = qq(
161                 SELECT
162                         virtualshelves.shelfnumber,
163                         virtualshelves.shelfname,
164                         owner,
165                         CONCAT(firstname, ' ', surname) AS name,
166                         virtualshelves.category,
167                         count(virtualshelfcontents.biblionumber) AS count
168                 FROM   virtualshelves
169                         LEFT JOIN  virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
170                         LEFT JOIN             borrowers ON virtualshelves.owner = borrowers.borrowernumber );
171     $query .= ($mincategory == 1) ? "WHERE  owner=? AND category=?" : "WHERE category>=?";
172         $query .= qq(
173                 GROUP BY virtualshelves.shelfnumber
174                 ORDER BY virtualshelves.category
175                 DESC 
176                 LIMIT ?, ?);
177         my $sth2 = $dbh->prepare($query);
178         $sth2->execute(@params);
179     my $shelves = $sth2->fetchall_arrayref({});
180     return ($shelves, $total);
181
182         # Probably NOT the final implementation since it is still bulky (repeated hash keys).
183         # might like an array of rows of delimited values:
184         # 1|2||0|blacklist|112
185         # 2|6|Josh Ferraro|51|en_fuego|106
186 }
187
188 =item GetRecentShelves
189
190         ($shelflist) = GetRecentShelves(1, $limit, $owner)
191
192 This function returns a references to an array of hashrefs containing specified shelves sorted
193 by the date the shelf was last modified in descending order limited to the number of records
194 specified by C<$row_count>. If calling with C<$mincategory> other than 1, use undef as C<$owner>.
195
196 This function is intended to return a dataset reflecting the most recently active shelves for
197 the submitted parameters.
198
199 =cut
200
201 sub GetRecentShelves ($$$) {
202         my ($mincategory, $row_count, $owner) = @_;
203     my (@shelflist);
204         my $total = _shelf_count($owner, $mincategory);
205         my @params = ($owner, $mincategory, 0, $row_count);      #FIXME: offset is hardcoded here, but could be passed in for enhancements
206         shift @params if !$owner;
207         my $query = "SELECT * FROM virtualshelves";
208         $query .= ($owner ? " WHERE owner = ? AND category = ?" : " WHERE category >= ? ");
209         $query .= " ORDER BY lastmodified DESC LIMIT ?, ?";
210         my $sth = $dbh->prepare($query);
211         $sth->execute(@params);
212         @shelflist = $sth->fetchall_arrayref({});
213         return ( \@shelflist, $total );
214 }
215
216 =item GetShelf
217
218   (shelfnumber,shelfname,owner,category,sortfield) = &GetShelf($shelfnumber);
219
220 Looks up information about the contents of virtual virtualshelves number
221 C<$shelfnumber>
222
223 Returns the database's information on 'virtualshelves' table.
224
225 =cut
226
227 sub GetShelf ($) {
228     my ($shelfnumber) = @_;
229     my $query = qq(
230         SELECT shelfnumber, shelfname, owner, category, sortfield
231         FROM   virtualshelves
232         WHERE  shelfnumber=?
233     );
234     my $sth = $dbh->prepare($query);
235     $sth->execute($shelfnumber);
236     return $sth->fetchrow;
237 }
238
239 =item GetShelfContents
240
241   $itemlist = &GetShelfContents($shelfnumber);
242
243 Looks up information about the contents of virtual virtualshelves number
244 C<$shelfnumber>.  Sorted by a field in the biblio table.  copyrightdate 
245 gives a desc sort.
246
247 Returns a reference-to-array, whose elements are references-to-hash,
248 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
249
250 Note: the notforloan status comes from the itemtype, and where it equals 0
251 it does not ensure that related items.notforloan status is likewise 0. The
252 caller has to check any items on their own, possibly with CanBookBeIssued
253 from C4::Circulation.
254
255 =cut
256
257 sub GetShelfContents ($$;$$) {
258     my ($shelfnumber, $row_count, $offset, $sortfield) = @_;
259     my $dbh=C4::Context->dbh();
260         my $sth1 = $dbh->prepare("SELECT count(*) FROM virtualshelfcontents WHERE shelfnumber = ?");
261         $sth1->execute($shelfnumber);
262         my $total = $sth1->fetchrow;
263         if(!$sortfield) {
264                 my $sth2 = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
265                 $sth2->execute($shelfnumber);
266                 ($sortfield) = $sth2->fetchrow_array;
267         }
268     my $query =
269        " SELECT vc.biblionumber, vc.shelfnumber, vc.dateadded,
270                                 biblio.*, biblioitems.itemtype, itemtypes.*
271          FROM   virtualshelfcontents vc
272                  LEFT JOIN biblio      ON      vc.biblionumber =      biblio.biblionumber
273                  LEFT JOIN biblioitems ON  biblio.biblionumber = biblioitems.biblionumber
274                  LEFT JOIN itemtypes   ON biblioitems.itemtype = itemtypes.itemtype
275          WHERE  vc.shelfnumber=? ";
276         my @params = ($shelfnumber);
277         if($sortfield) {
278                 $query .= " ORDER BY ? ";
279                 $query .= " DESC " if ($sortfield eq 'copyrightdate');
280                 push (@params, $sortfield);
281         }
282         $query .= " LIMIT ?, ? ";
283         push (@params, ($offset ? $offset : 0));
284         push (@params, $row_count);
285     my $sth3 = $dbh->prepare($query);
286         $sth3->execute(@params);
287         return ($sth3->fetchall_arrayref({}), $total);
288         # Like the perldoc says,
289         # returns reference-to-array, where each element is reference-to-hash of the row:
290         #   like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ] 
291         # Suitable for use in TMPL_LOOP.
292         # See http://search.cpan.org/~timb/DBI-1.601/DBI.pm#fetchall_arrayref
293         # or newer, for your version of DBI.
294 }
295
296 =item AddShelf
297
298   $shelfnumber = &AddShelf( $shelfname, $owner, $category);
299
300 Creates a new virtual virtualshelves with name C<$shelfname>, owner C<$owner> and category
301 C<$category>.
302
303 Returns a code to know what's happen.
304     * -1 : if this virtualshelves already exist.
305     * $shelfnumber : if success.
306
307 =cut
308
309 sub AddShelf {
310     my ( $shelfname, $owner, $category, $sortfield ) = @_;
311     my $query = qq(
312         SELECT *
313         FROM   virtualshelves
314         WHERE  shelfname=? AND owner=?
315     );
316     my $sth = $dbh->prepare($query);
317     $sth->execute($shelfname,$owner);
318     ( $sth->rows ) and return (-1);
319     $query = qq(
320         INSERT INTO virtualshelves
321             (shelfname,owner,category,sortfield)
322         VALUES (?,?,?,?)
323     );
324     $sth = $dbh->prepare($query);
325     $sth->execute( $shelfname, $owner, $category, $sortfield );
326     my $shelfnumber = $dbh->{'mysql_insertid'};
327     return ($shelfnumber);
328 }
329
330 =item AddToShelf
331
332   &AddToShelf($biblionumber, $shelfnumber);
333
334 Adds item number C<$biblionumber> to virtual virtualshelves number
335 C<$shelfnumber>, unless that item is already on that shelf.
336
337 =cut
338
339 #'
340 sub AddToShelf {
341     my ( $biblionumber, $shelfnumber ) = @_;
342     return unless $biblionumber;
343     my $query = qq(
344         SELECT *
345         FROM   virtualshelfcontents
346         WHERE  shelfnumber=? AND biblionumber=?
347     );
348     my $sth = $dbh->prepare($query);
349
350     $sth->execute( $shelfnumber, $biblionumber );
351     ($sth->rows) and return undef;      # already on shelf
352         $query = qq(
353                 INSERT INTO virtualshelfcontents
354                         (shelfnumber, biblionumber, flags)
355                 VALUES
356                         (?, ?, 0)
357         );
358         $sth = $dbh->prepare($query);
359         $sth->execute( $shelfnumber, $biblionumber );
360         $query = qq(UPDATE virtualshelves
361                                 SET lastmodified = CURRENT_TIMESTAMP
362                                 WHERE shelfnumber = ?);
363         $sth = $dbh->prepare($query);
364         $sth->execute( $shelfnumber );
365 }
366
367 =item AddToShelfFromBiblio
368  
369     &AddToShelfFromBiblio($biblionumber, $shelfnumber)
370
371     this function allow to add a virtual into the shelf number $shelfnumber
372     from biblionumber.
373
374 =cut
375
376 sub AddToShelfFromBiblio {
377     my ( $biblionumber, $shelfnumber ) = @_;
378     return unless $biblionumber;
379     my $query = qq(
380         SELECT *
381         FROM   virtualshelfcontents
382         WHERE  shelfnumber=? AND biblionumber=?
383     );
384     my $sth = $dbh->prepare($query);
385     $sth->execute( $shelfnumber, $biblionumber );
386     unless ( $sth->rows ) {
387         my $query =qq(
388             INSERT INTO virtualshelfcontents
389                 (shelfnumber, biblionumber, flags)
390             VALUES
391                 (?, ?, 0)
392         );
393         $sth = $dbh->prepare($query);
394         $sth->execute( $shelfnumber, $biblionumber );
395                 $query = qq(UPDATE virtualshelves
396                                         SET lastmodified = CURRENT_TIMESTAMP
397                                         WHERE shelfnumber = ?);
398                 $sth = $dbh->prepare($query);
399                 $sth->execute( $shelfnumber );
400     }
401 }
402
403 =item ModShelf
404
405 ModShelf($shelfnumber, $hashref)
406
407 Where $hashref->{column} = param
408
409 Modify the value into virtualshelves table with values given 
410 from hashref, which each key of the hashref should be
411 the name of a column of virtualshelves.
412
413 =cut
414
415 sub ModShelf {
416     my $shelfnumber = shift;
417     my $shelf = shift;
418
419     if (exists $shelf->{shelfnumber}) {
420         carp "Should not use ModShelf to change shelfnumber";
421         return;
422     }
423     unless (defined $shelfnumber and $shelfnumber =~ /^\d+$/) {
424         carp "Invalid shelfnumber passed to ModShelf: $shelfnumber";
425         return;
426     }
427
428         my $query = "UPDATE virtualshelves SET ";
429     my @bind_params = ();
430     my @set_clauses = ();
431
432         foreach my $column (keys %$shelf) {
433         push @set_clauses, "$column = ?";
434         push @bind_params, $shelf->{$column};
435     }
436
437     if ($#set_clauses == -1) {
438         carp "No columns to update passed to ModShelf";
439         return;
440     }
441     $query .= join(", ", @set_clauses);
442
443     $query .= " WHERE shelfnumber = ? ";
444     push @bind_params, $shelfnumber;
445
446     $debug and warn "ModShelf query:\n $query\n",
447                         "ModShelf query args: ", join(',', @bind_params), "\n";
448         my $sth = $dbh->prepare($query);
449         $sth->execute( @bind_params );
450 }
451
452 =item ShelfPossibleAction
453
454 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
455
456 C<$loggedinuser,$shelfnumber,$action>
457
458 $action can be "view" or "manage".
459
460 Returns 1 if the user can do the $action in the $shelfnumber shelf.
461 Returns 0 otherwise.
462
463 =cut
464
465 sub ShelfPossibleAction {
466     my ( $user, $shelfnumber, $action ) = @_;
467     my $query = qq(
468         SELECT owner,category
469         FROM   virtualshelves
470         WHERE  shelfnumber=?
471     );
472     my $sth = $dbh->prepare($query);
473     $sth->execute($shelfnumber);
474     my ( $owner, $category ) = $sth->fetchrow;
475         my $borrower = GetMemberDetails($user);
476         return 1 if ( $category >= 3);                                                  # open list
477     return 1 if (($category >= 2) and
478                                 defined($action) and $action eq 'view');        # public list, anybody can view
479     return 1 if (($category >= 2) and defined($user) and ($borrower->{authflags}->{superlibrarian} || $user == 0));     # public list, superlibrarian can edit/delete
480     return 1 if (defined($user)  and $owner  eq $user );        # user owns this list.  Check last.
481     return 0;
482 }
483
484 =item DelFromShelf
485
486   &DelFromShelf( $biblionumber, $shelfnumber);
487
488 Removes item number C<$biblionumber> from virtual virtualshelves number
489 C<$shelfnumber>. If the item wasn't on that virtualshelves to begin with,
490 nothing happens.
491
492 =cut
493
494 #'
495 sub DelFromShelf {
496     my ( $biblionumber, $shelfnumber ) = @_;
497     my $query = qq(
498         DELETE FROM virtualshelfcontents
499         WHERE  shelfnumber=? AND biblionumber=?
500     );
501     my $sth = $dbh->prepare($query);
502     $sth->execute( $shelfnumber, $biblionumber );
503 }
504
505 =item DelShelf (old version)
506
507   ($status, $msg) = &DelShelf($shelfnumber);
508
509 Deletes virtual virtualshelves number C<$shelfnumber>. The virtualshelves must
510 be empty.
511
512 Returns a two-element array, where C<$status> is 0 if the operation
513 was successful, or non-zero otherwise. C<$msg> is "Done" in case of
514 success, or an error message giving the reason for failure.
515
516 =item DelShelf (current version)
517
518   $Number = DelShelf($shelfnumber);
519
520 This function deletes the shelf number, and all of it's content.
521
522 =cut
523
524 sub DelShelf {
525         unless (@_) {
526                 carp "DelShelf called without valid argument (shelfnumber)";
527                 return undef;
528         }
529         my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?");
530         return $sth->execute(shift);
531 }
532
533 =item RefreshShelvesSummary
534
535         ($total, $pubshelves, $barshelves) = RefreshShelvesSummary($sessionID, $loggedinuser, $row_count);
536
537 Updates the current session and userenv with the most recent shelves
538
539 Returns the total number of shelves stored in the session/userenv along with two references each to an
540 array of hashes, one containing the C<$loggedinuser>'s private shelves and one containing all public/open shelves.
541
542 This function is used in conjunction with the 'Lists' button in masthead.inc.
543
544 =cut
545
546 sub RefreshShelvesSummary ($$$) {
547         
548         my ($sessionID, $loggedinuser, $row_count) = @_;
549         my $session = get_session($sessionID);
550         my ($total, $totshelves, $barshelves, $pubshelves);
551
552         ($barshelves, $totshelves) = GetRecentShelves(1, $row_count, $loggedinuser);
553         $total->{'bartotal'} = $totshelves;
554         ($pubshelves, $totshelves) = GetRecentShelves(2, $row_count, undef);
555         $total->{'pubtotal'} = $totshelves;
556
557         # Update the current session with the latest shelves...
558         $session->param('barshelves', $barshelves->[0]);
559         $session->param('pubshelves', $pubshelves->[0]);
560         $session->param('totshelves', $total);
561
562         # likewise the userenv...
563         C4::Context->set_shelves_userenv('bar',$barshelves->[0]);
564         C4::Context->set_shelves_userenv('pub',$pubshelves->[0]);
565         C4::Context::set_shelves_userenv('tot',$total);
566
567         return ($total, $pubshelves, $barshelves);
568 }
569
570 # internal subs
571
572 sub _shelf_count ($$) {
573         my (@params) = @_;
574         # Find out how many shelves total meet the submitted criteria...
575         my $query = "SELECT count(*) FROM virtualshelves";
576         $query .= ($params[1] > 1) ? " WHERE category >= ?" : " WHERE  owner=? AND category=?";
577         shift @params if $params[1] > 1;
578         my $sth = $dbh->prepare($query);
579         $sth->execute(@params);
580         my $total = $sth->fetchrow;
581         return $total;
582 }
583
584 1;
585
586 __END__
587
588 =back
589
590 =head1 AUTHOR
591
592 Koha Developement team <info@koha.org>
593
594 =head1 SEE ALSO
595
596 C4::Circulation::Circ2(3)
597
598 =cut