Allow anyone with superlibrarian permission to edit/delete public patron lists
[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
41         &AddToShelf &AddToShelfFromBiblio &AddShelf
42
43         &ModShelf
44         &ShelfPossibleAction
45         &DelFromShelf &DelShelf
46         );
47         @EXPORT_OK = qw(&GetShelvesSummary);
48 }
49
50 my $dbh = C4::Context->dbh;
51
52 =head1 NAME
53
54 C4::VirtualShelves - Functions for manipulating Koha virtual virtualshelves
55
56 =head1 SYNOPSIS
57
58   use C4::VirtualShelves;
59
60 =head1 DESCRIPTION
61
62 This module provides functions for manipulating virtual virtualshelves,
63 including creating and deleting virtualshelves, and adding and removing
64 items to and from virtualshelves.
65
66 =head1 FUNCTIONS
67
68 =over 2
69
70 =item GetShelves
71
72   $shelflist = &GetShelves($owner);
73   $shelflist = &GetShelves($owner, $mincategory);
74   $shelflist = &GetShelves($owner, $mincategory, $limit);
75   ($shelfnumber, $shelfhash) = each %{$shelflist};
76
77 Looks up the virtual virtualshelves, and returns a summary. C<$shelflist>
78 is a reference-to-hash. The keys are the virtualshelves numbers
79 (C<$shelfnumber>, above), and the values (C<$shelfhash>, above) are
80 themselves references-to-hash, with the following keys:
81
82 C<mincategory> : 2 if the list is for "Public", 3 for "Open".
83 virtualshelves of the owner are always selected, whatever the category
84
85 =over 4
86
87 =item C<$shelfhash-E<gt>{shelfname}>
88
89 A string. The name of the shelf.
90
91 =item C<$shelfhash-E<gt>{count}>
92
93 The number of virtuals on that virtualshelves.
94
95 =back
96
97 =cut
98
99 sub GetShelves {
100     my ($owner, $mincategory, $limit) = @_;
101         ($mincategory and $mincategory =~ /^\d+$/) or $mincategory = 2;
102         (      $limit and       $limit =~ /^\d+$/) or $limit = undef;
103     my $query = qq(
104         SELECT virtualshelves.shelfnumber, virtualshelves.shelfname,owner,surname,firstname,virtualshelves.category,virtualshelves.sortfield,
105                count(virtualshelfcontents.biblionumber) as count
106         FROM   virtualshelves
107             LEFT JOIN   virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
108             LEFT JOIN   borrowers ON virtualshelves.owner = borrowers.borrowernumber
109         WHERE  owner=? OR category>=?
110         GROUP BY virtualshelves.shelfnumber
111         ORDER BY virtualshelves.category, virtualshelves.shelfname, borrowers.firstname, borrowers.surname
112     );
113         $limit and $query .= " LIMIT $limit ";
114     my $sth = $dbh->prepare($query);
115     $sth->execute( $owner, $mincategory );
116     my %shelflist;
117     while (
118         my (
119             $shelfnumber, $shelfname, $owner, $surname,
120             $firstname,   $category,  $sortfield, $count
121         )
122         = $sth->fetchrow
123       )
124     {
125         $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
126         $shelflist{$shelfnumber}->{'count'}     = $count;
127         $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
128         $shelflist{$shelfnumber}->{'category'}  = $category;
129         $shelflist{$shelfnumber}->{'owner'}     = $owner;
130         $shelflist{$shelfnumber}->{'surname'}   = $surname;
131         $shelflist{$shelfnumber}->{'firstname'} = $firstname;
132     }
133     return ( \%shelflist );
134 }
135
136 sub GetShelvesSummary {
137     my ($owner, $mincategory, $limit) = @_;
138         ($mincategory and $mincategory =~ /^\d+$/) or $mincategory = 2;
139         (      $limit and       $limit =~ /^\d+$/) or $limit = 10;
140     my $query = qq(
141                 SELECT
142                         virtualshelves.shelfnumber,
143                         virtualshelves.shelfname,
144                         owner,
145                         CONCAT(firstname, ' ', surname) AS name,
146                         virtualshelves.category,
147                         count(virtualshelfcontents.biblionumber) AS count
148                 FROM   virtualshelves
149                         LEFT JOIN  virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
150                         LEFT JOIN             borrowers ON virtualshelves.owner = borrowers.borrowernumber
151                 WHERE  owner=? OR category>=?
152                 GROUP BY virtualshelves.shelfnumber
153                 ORDER BY virtualshelves.category, borrowers.surname, borrowers.firstname, virtualshelves.shelfname
154                 LIMIT ?
155         );
156         my $sth = $dbh->prepare($query);
157         $sth->execute($owner,$mincategory,$limit);
158
159     my $shelves = $sth->fetchall_arrayref({});
160     # add private flag to each shelf entry --
161     # need to do this because HTML::Template::Pro's EXPR
162     # support complains about a non-initialized 'category'
163     # if the user has no shelves -- the offending line in
164     # masthead.inc was <-- TMPL_IF EXPR="category == 1"...
165     foreach my $shelf (@{ $shelves }) {
166         $shelf->{'private'} = ($shelf->{'category'} == 1);
167     }
168     return $shelves;
169
170         # Probably NOT the final implementation since it is still bulky (repeated hash keys).
171         # might like an array of rows of delimited values:
172         # 1|2||0|blacklist|112
173         # 2|6|Josh Ferraro|51|en_fuego|106
174 }
175
176 =item GetShelf
177
178   (shelfnumber,shelfname,owner,category,sortfield) = &GetShelf($shelfnumber);
179
180 Looks up information about the contents of virtual virtualshelves number
181 C<$shelfnumber>
182
183 Returns the database's information on 'virtualshelves' table.
184
185 =cut
186
187 sub GetShelf {
188     my ($shelfnumber) = @_;
189     my $query = qq(
190         SELECT shelfnumber, shelfname, owner, category, sortfield
191         FROM   virtualshelves
192         WHERE  shelfnumber=?
193     );
194     my $sth = $dbh->prepare($query);
195     $sth->execute($shelfnumber);
196     return $sth->fetchrow;
197 }
198
199 =item GetShelfContents
200
201   $itemlist = &GetShelfContents($shelfnumber);
202
203 Looks up information about the contents of virtual virtualshelves number
204 C<$shelfnumber>.  Sorted by a field in the biblio table.  copyrightdate 
205 gives a desc sort.
206
207 Returns a reference-to-array, whose elements are references-to-hash,
208 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
209
210 Note: the notforloan status comes from the itemtype, and where it equals 0
211 it does not ensure that related items.notforloan status is likewise 0. The
212 caller has to check any items on their own, possibly with CanBookBeIssued
213 from C4::Circulation.
214
215 =cut
216
217 sub GetShelfContents {
218     my ( $shelfnumber ,$sortfield) = @_;
219     my $dbh=C4::Context->dbh();
220         if(!$sortfield) {
221                 my $sthsort = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
222                 $sthsort->execute($shelfnumber);
223                 ($sortfield) = $sthsort->fetchrow_array;
224         }
225     my $query =
226        " SELECT vc.biblionumber, vc.shelfnumber, vc.dateadded,
227                                 biblio.*, biblioitems.itemtype, itemtypes.*
228          FROM   virtualshelfcontents vc
229                  LEFT JOIN biblio      ON      vc.biblionumber =      biblio.biblionumber
230                  LEFT JOIN biblioitems ON  biblio.biblionumber = biblioitems.biblionumber
231                  LEFT JOIN itemtypes   ON biblioitems.itemtype = itemtypes.itemtype
232          WHERE  vc.shelfnumber=? ";
233         if($sortfield) {
234                 $query .= " ORDER BY `$sortfield` ";
235                 $query .= " DESC " if ($sortfield eq 'copyrightdate');
236         }
237     my $sth = $dbh->prepare($query);
238         $sth->execute($shelfnumber);
239         return $sth->fetchall_arrayref({});     
240         # Like the perldoc says,
241         # returns reference-to-array, where each element is reference-to-hash of the row:
242         #   like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ] 
243         # Suitable for use in TMPL_LOOP.
244         # See http://search.cpan.org/~timb/DBI-1.601/DBI.pm#fetchall_arrayref
245         # or newer, for your version of DBI.
246 }
247
248 =item AddShelf
249
250   $shelfnumber = &AddShelf( $shelfname, $owner, $category);
251
252 Creates a new virtual virtualshelves with name C<$shelfname>, owner C<$owner> and category
253 C<$category>.
254
255 Returns a code to know what's happen.
256     * -1 : if this virtualshelves already exist.
257     * $shelfnumber : if success.
258
259 =cut
260
261 sub AddShelf {
262     my ( $shelfname, $owner, $category ) = @_;
263     my $query = qq(
264         SELECT *
265         FROM   virtualshelves
266         WHERE  shelfname=? AND owner=?
267     );
268     my $sth = $dbh->prepare($query);
269     $sth->execute($shelfname,$owner);
270     ( $sth->rows ) and return (-1);
271     $query = qq(
272         INSERT INTO virtualshelves
273             (shelfname,owner,category)
274         VALUES (?,?,?)
275     );
276     $sth = $dbh->prepare($query);
277     $sth->execute( $shelfname, $owner, $category );
278     my $shelfnumber = $dbh->{'mysql_insertid'};
279     return ($shelfnumber);
280 }
281
282 =item AddToShelf
283
284   &AddToShelf($biblionumber, $shelfnumber);
285
286 Adds item number C<$biblionumber> to virtual virtualshelves number
287 C<$shelfnumber>, unless that item is already on that shelf.
288
289 =cut
290
291 #'
292 sub AddToShelf {
293     my ( $biblionumber, $shelfnumber ) = @_;
294     return unless $biblionumber;
295     my $query = qq(
296         SELECT *
297         FROM   virtualshelfcontents
298         WHERE  shelfnumber=? AND biblionumber=?
299     );
300     my $sth = $dbh->prepare($query);
301
302     $sth->execute( $shelfnumber, $biblionumber );
303     ($sth->rows) and return undef;      # already on shelf
304         $query = qq(
305                 INSERT INTO virtualshelfcontents
306                         (shelfnumber, biblionumber, flags)
307                 VALUES
308                         (?, ?, 0)
309         );
310         $sth = $dbh->prepare($query);
311         $sth->execute( $shelfnumber, $biblionumber );
312 }
313
314 =item AddToShelfFromBiblio
315  
316     &AddToShelfFromBiblio($biblionumber, $shelfnumber)
317
318     this function allow to add a virtual into the shelf number $shelfnumber
319     from biblionumber.
320
321 =cut
322
323 sub AddToShelfFromBiblio {
324     my ( $biblionumber, $shelfnumber ) = @_;
325     return unless $biblionumber;
326     my $query = qq(
327         SELECT *
328         FROM   virtualshelfcontents
329         WHERE  shelfnumber=? AND biblionumber=?
330     );
331     my $sth = $dbh->prepare($query);
332     $sth->execute( $shelfnumber, $biblionumber );
333     unless ( $sth->rows ) {
334         my $query =qq(
335             INSERT INTO virtualshelfcontents
336                 (shelfnumber, biblionumber, flags)
337             VALUES
338                 (?, ?, 0)
339         );
340         $sth = $dbh->prepare($query);
341         $sth->execute( $shelfnumber, $biblionumber );
342     }
343 }
344
345 =item ModShelf
346
347 ModShelf($shelfnumber, $hashref)
348
349 Where $hashref->{column} = param
350
351 Modify the value into virtualshelves table with values given 
352 from hashref, which each key of the hashref should be
353 the name of a column of virtualshelves.
354
355 =cut
356
357 sub ModShelf {
358     my $shelfnumber = shift;
359     my $shelf = shift;
360
361     if (exists $shelf->{shelfnumber}) {
362         carp "Should not use ModShelf to change shelfnumber";
363         return;
364     }
365     unless (defined $shelfnumber and $shelfnumber =~ /^\d+$/) {
366         carp "Invalid shelfnumber passed to ModShelf: $shelfnumber";
367         return;
368     }
369
370         my $query = "UPDATE virtualshelves SET ";
371     my @bind_params = ();
372     my @set_clauses = ();
373
374         foreach my $column (keys %$shelf) {
375         push @set_clauses, "$column = ?";
376         push @bind_params, $shelf->{$column};
377     }
378
379     if ($#set_clauses == -1) {
380         carp "No columns to update passed to ModShelf";
381         return;
382     }
383     $query .= join(", ", @set_clauses);
384
385     $query .= " WHERE shelfnumber = ? ";
386     push @bind_params, $shelfnumber;
387
388     $debug and warn "ModShelf query:\n $query\n",
389                         "ModShelf query args: ", join(',', @bind_params), "\n";
390         my $sth = $dbh->prepare($query);
391         $sth->execute( @bind_params );
392 }
393
394 =item ShelfPossibleAction
395
396 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
397
398 C<$loggedinuser,$shelfnumber,$action>
399
400 $action can be "view" or "manage".
401
402 Returns 1 if the user can do the $action in the $shelfnumber shelf.
403 Returns 0 otherwise.
404
405 =cut
406
407 sub ShelfPossibleAction {
408     my ( $user, $shelfnumber, $action ) = @_;
409     my $query = qq(
410         SELECT owner,category
411         FROM   virtualshelves
412         WHERE  shelfnumber=?
413     );
414     my $sth = $dbh->prepare($query);
415     $sth->execute($shelfnumber);
416     my ( $owner, $category ) = $sth->fetchrow;
417         my $borrower = GetMemberDetails($user);
418         return 1 if ( $category >= 3);                                                  # open list
419     return 1 if (($category >= 2) and
420                                 defined($action) and $action eq 'view');        # public list, anybody can view
421     return 1 if (($category >= 2) and defined($user) and $borrower->{authflags}->{superlibrarian});     # public list, superlibrarian can edit/delete
422     return 1 if (defined($user)  and $owner  eq $user );        # user owns this list.  Check last.
423     return 0;
424 }
425
426 =item DelFromShelf
427
428   &DelFromShelf( $biblionumber, $shelfnumber);
429
430 Removes item number C<$biblionumber> from virtual virtualshelves number
431 C<$shelfnumber>. If the item wasn't on that virtualshelves to begin with,
432 nothing happens.
433
434 =cut
435
436 #'
437 sub DelFromShelf {
438     my ( $biblionumber, $shelfnumber ) = @_;
439     my $query = qq(
440         DELETE FROM virtualshelfcontents
441         WHERE  shelfnumber=? AND biblionumber=?
442     );
443     my $sth = $dbh->prepare($query);
444     $sth->execute( $shelfnumber, $biblionumber );
445 }
446
447 =item DelShelf (old version)
448
449   ($status, $msg) = &DelShelf($shelfnumber);
450
451 Deletes virtual virtualshelves number C<$shelfnumber>. The virtualshelves must
452 be empty.
453
454 Returns a two-element array, where C<$status> is 0 if the operation
455 was successful, or non-zero otherwise. C<$msg> is "Done" in case of
456 success, or an error message giving the reason for failure.
457
458 =item DelShelf (current version)
459
460   $Number = DelShelf($shelfnumber);
461
462 This function deletes the shelf number, and all of it's content.
463
464 =cut
465
466 sub DelShelf {
467         unless (@_) {
468                 carp "DelShelf called without valid argument (shelfnumber)";
469                 return undef;
470         }
471         my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?");
472         return $sth->execute(shift);
473 }
474
475 1;
476
477 __END__
478
479 =back
480
481 =head1 AUTHOR
482
483 Koha Developement team <info@koha.org>
484
485 =head1 SEE ALSO
486
487 C4::Circulation::Circ2(3)
488
489 =cut