Bug 7310: Code changes for Improving list permissions
[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
21 # with Koha; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24 use strict;
25 use warnings;
26
27 use Carp;
28 use C4::Context;
29 use C4::Debug;
30
31 use constant SHELVES_MASTHEAD_MAX => 10; #number under Lists button in masthead
32 use constant SHELVES_COMBO_MAX => 10; #add to combo in search
33 use constant SHELVES_MGRPAGE_MAX => 20; #managing page
34 use constant SHELVES_POPUP_MAX => 40; #addbybiblio popup
35
36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
37
38 BEGIN {
39         # set the version for version checking
40         $VERSION = 3.02;
41         require Exporter;
42         @ISA    = qw(Exporter);
43         @EXPORT = qw(
44             &GetShelves &GetShelfContents &GetShelf
45             &AddToShelf &AddShelf
46             &ModShelf
47             &ShelfPossibleAction
48             &DelFromShelf &DelShelf
49             &GetBibliosShelves
50         );
51         @EXPORT_OK = qw(
52             &GetAllShelves &ShelvesMax
53         );
54 }
55
56
57 my $dbh = C4::Context->dbh;
58
59 =head1 NAME
60
61 C4::VirtualShelves - Functions for manipulating Koha virtual shelves
62
63 =head1 SYNOPSIS
64
65   use C4::VirtualShelves;
66
67 =head1 DESCRIPTION
68
69 This module provides functions for manipulating virtual shelves,
70 including creating and deleting virtual shelves, and adding and removing
71 bibs to and from virtual shelves.
72
73 =head1 FUNCTIONS
74
75 =head2 GetShelves
76
77   ($shelflist, $totshelves) = &GetShelves($category, $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<$category> criteria.  C<$category>,
82 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$category> == 1.
83 When C<$category> is 2, supply undef as argument for C<$owner>.
84
85 This function is used by shelfpage in VirtualShelves/Page.pm when listing all shelves for lists management in opac or staff client. Order is by shelfname.
86
87 C<$shelflist>is a reference-to-hash. The keys are the virtualshelves numbers (C<$shelfnumber>, above),
88 and the values (C<$shelfhash>, above) are themselves references-to-hash, with the following keys:
89
90 =over
91
92 =item C<$shelfhash-E<gt>{shelfname}>
93
94 A string. The name of the shelf.
95
96 =item C<$shelfhash-E<gt>{count}>
97
98 The number of virtuals on that virtualshelves.
99
100 =back
101
102 =cut
103
104 sub GetShelves {
105     my ($category, $row_count, $offset, $owner) = @_;
106     my @params;
107     my $total = _shelf_count($owner, $category);
108     my $query = qq{
109         SELECT vs.shelfnumber, vs.shelfname,vs.owner,
110         bo.surname,bo.firstname,vs.category,vs.sortfield,
111         count(vc.biblionumber) as count
112         FROM virtualshelves vs
113         LEFT JOIN borrowers bo ON vs.owner=bo.borrowernumber
114         LEFT JOIN virtualshelfcontents vc USING (shelfnumber) };
115     if($category==1) {
116         $query.= qq{
117             LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
118             AND sh.borrowernumber=?
119             WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
120         @params= ($owner, $owner, $owner, $offset||0, $row_count);
121     }
122     else {
123         $query.= 'WHERE category=2 ';
124         @params= ($offset||0, $row_count);
125     }
126     $query.= qq{
127         GROUP BY vs.shelfnumber
128         ORDER BY vs.shelfname
129         LIMIT ?, ?};
130
131     my $sth2 = $dbh->prepare($query);
132     $sth2->execute(@params);
133     my %shelflist;
134     while( my ($shelfnumber, $shelfname, $owner, $surname, $firstname, $category, $sortfield, $count)= $sth2->fetchrow) {
135         $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
136         $shelflist{$shelfnumber}->{'count'}     = $count;
137         $shelflist{$shelfnumber}->{'single'}    = $count==1;
138         $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
139         $shelflist{$shelfnumber}->{'category'}  = $category;
140         $shelflist{$shelfnumber}->{'owner'}     = $owner;
141         $shelflist{$shelfnumber}->{'surname'}   = $surname;
142         $shelflist{$shelfnumber}->{'firstname'} = $firstname;
143     }
144     return ( \%shelflist, $total );
145 }
146
147 =head2 GetAllShelves
148
149     $shelflist = GetAllShelves($category, $owner)
150
151 This function returns a reference to an array of hashrefs containing all shelves
152 sorted by the shelf name.
153
154 This function is intended to return a dataset reflecting all the shelves for
155 the submitted parameters.
156
157 =cut
158
159 sub GetAllShelves {
160     my ($category,$owner,$adding_allowed) = @_;
161     my @params;
162     my $query = 'SELECT vs.* FROM virtualshelves vs ';
163     if($category==1) {
164         $query.= qq{
165             LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
166             AND sh.borrowernumber=?
167             WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
168         @params = ($owner, $owner, $owner);
169     }
170     else {
171         $query.='WHERE category=2 ';
172         @params = ();
173     }
174     $query.='AND (allow_add=1 OR owner=?) ' if $adding_allowed;
175     push @params, $owner if $adding_allowed;
176     $query.= 'ORDER BY shelfname ASC';
177     my $sth = $dbh->prepare( $query );
178     $sth->execute(@params);
179     return $sth->fetchall_arrayref({});
180 }
181
182 =head2 GetSomeShelfNames
183
184 Returns shelf names and numbers for Add to combo of search results and Lists button of OPAC header.
185
186 =cut
187
188 sub GetSomeShelfNames {
189     my ($owner, $purpose, $adding_allowed)= @_;
190     my ($bar, $pub, @params);
191
192     my $bquery = 'SELECT vs.shelfnumber, vs.shelfname FROM virtualshelves vs ';
193     my $limit= ShelvesMax($purpose);
194
195     my $qry1= $bquery."WHERE vs.category=2 ";
196     $qry1.= "AND (allow_add=1 OR owner=?) " if $adding_allowed;
197     push @params, $owner||0 if $adding_allowed;
198     $qry1.= "ORDER BY vs.lastmodified DESC LIMIT $limit";
199
200     unless($adding_allowed && (!defined($owner) || $owner<=0)) {
201       #if adding items, user should be known
202       $pub= $dbh->selectall_arrayref($qry1,{Slice=>{}},@params);
203     }
204
205     if($owner) {
206       my $qry2= $bquery. qq{
207         LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber AND sh.borrowernumber=?
208         WHERE vs.category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
209       @params=($owner,$owner,$owner);
210       $qry2.= "AND (allow_add=1 OR owner=?) " if $adding_allowed;
211       push @params, $owner if $adding_allowed;
212       $qry2.= "ORDER BY vs.lastmodified DESC ";
213       $qry2.= "LIMIT $limit";
214       $bar= $dbh->selectall_arrayref($qry2,{Slice=>{}},@params);
215     }
216
217     return ( { bartotal => $bar? scalar @$bar: 0, pubtotal => $pub? scalar @$pub: 0}, $pub, $bar);
218 }
219
220 =head2 GetShelf
221
222   (shelfnumber,shelfname,owner,category,sortfield) = &GetShelf($shelfnumber);
223
224 Looks up information about the contents of virtual virtualshelves number
225 C<$shelfnumber>
226
227 Returns the database's information on 'virtualshelves' table.
228
229 =cut
230
231 sub GetShelf ($) {
232     my ($shelfnumber) = @_;
233     my $query = qq(
234         SELECT shelfnumber, shelfname, owner, category, sortfield
235         FROM   virtualshelves
236         WHERE  shelfnumber=?
237     );
238     my $sth = $dbh->prepare($query);
239     $sth->execute($shelfnumber);
240     return $sth->fetchrow;
241 }
242
243 =head2 GetShelfContents
244
245   $biblist = &GetShelfContents($shelfnumber);
246
247 Looks up information about the contents of virtual virtualshelves number
248 C<$shelfnumber>.  Sorted by a field in the biblio table.  copyrightdate 
249 gives a desc sort.
250
251 Returns a reference-to-array, whose elements are references-to-hash,
252 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
253
254 Note: the notforloan status comes from the itemtype, and where it equals 0
255 it does not ensure that related items.notforloan status is likewise 0. The
256 caller has to check any items on their own, possibly with CanBookBeIssued
257 from C4::Circulation.
258
259 =cut
260
261 sub GetShelfContents ($;$$$) {
262     my ($shelfnumber, $row_count, $offset, $sortfield) = @_;
263     my $dbh=C4::Context->dbh();
264         my $sth1 = $dbh->prepare("SELECT count(*) FROM virtualshelfcontents WHERE shelfnumber = ?");
265         $sth1->execute($shelfnumber);
266         my $total = $sth1->fetchrow;
267         if(!$sortfield) {
268                 my $sth2 = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
269                 $sth2->execute($shelfnumber);
270                 ($sortfield) = $sth2->fetchrow_array;
271         }
272     my $query =
273        " SELECT vc.biblionumber, vc.shelfnumber, vc.dateadded, itemtypes.*,
274             biblio.*, biblioitems.itemtype, biblioitems.publicationyear as year, biblioitems.publishercode, biblioitems.place, biblioitems.size, biblioitems.pages
275          FROM   virtualshelfcontents vc
276                  LEFT JOIN biblio      ON      vc.biblionumber =      biblio.biblionumber
277                  LEFT JOIN biblioitems ON  biblio.biblionumber = biblioitems.biblionumber
278                  LEFT JOIN itemtypes   ON biblioitems.itemtype = itemtypes.itemtype
279          WHERE  vc.shelfnumber=? ";
280         my @params = ($shelfnumber);
281         if($sortfield) {
282                 $query .= " ORDER BY " . $sortfield;
283                 $query .= " DESC " if ($sortfield eq 'copyrightdate');
284         }
285     if($row_count){
286            $query .= " LIMIT ?, ? ";
287            push (@params, ($offset ? $offset : 0));
288            push (@params, $row_count);
289     }
290     my $sth3 = $dbh->prepare($query);
291         $sth3->execute(@params);
292         return ($sth3->fetchall_arrayref({}), $total);
293         # Like the perldoc says,
294         # returns reference-to-array, where each element is reference-to-hash of the row:
295         #   like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ] 
296         # Suitable for use in TMPL_LOOP.
297         # See http://search.cpan.org/~timb/DBI-1.601/DBI.pm#fetchall_arrayref
298         # or newer, for your version of DBI.
299 }
300
301 =head2 AddShelf
302
303   $shelfnumber = &AddShelf($hashref, $owner);
304
305 Creates a new virtual shelf. Params passed in a hash like ModShelf.
306
307 Returns a code to know what's happen.
308     * -1 : if this virtualshelves already exists.
309     * $shelfnumber : if success.
310
311 =cut
312
313 sub AddShelf {
314     my ($hashref, $owner)= @_;
315
316     #initialize missing hash values to silence warnings
317     foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) {
318         $hashref->{$_}= exists $hashref->{$_}? $hashref->{$_}||'': '';
319     }
320
321     return -1 unless _CheckShelfName($hashref->{shelfname}, $hashref->{category}, $owner, 0);
322
323     my $query = qq(INSERT INTO virtualshelves
324         (shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other)
325         VALUES (?,?,?,?,?,?,?));
326
327     my $sth = $dbh->prepare($query);
328     $sth->execute(
329         $hashref->{shelfname},
330         $owner,
331         $hashref->{category},
332         $hashref->{sortfield},
333         $hashref->{allow_add}||0,
334         $hashref->{allow_delete_own}||1,
335         $hashref->{allow_delete_other}||0 );
336     my $shelfnumber = $dbh->{'mysql_insertid'};
337     return $shelfnumber;
338 }
339
340 =head2 AddToShelf
341
342   &AddToShelf($biblionumber, $shelfnumber, $borrower);
343
344 Adds bib number C<$biblionumber> to virtual virtualshelves number
345 C<$shelfnumber>, unless that bib is already on that shelf.
346
347 =cut
348
349 sub AddToShelf {
350     my ($biblionumber, $shelfnumber, $borrowernumber) = @_;
351     return unless $biblionumber;
352     my $query = qq(
353         SELECT *
354         FROM   virtualshelfcontents
355         WHERE  shelfnumber=? AND biblionumber=?
356     );
357     my $sth = $dbh->prepare($query);
358
359     $sth->execute( $shelfnumber, $biblionumber );
360     ($sth->rows) and return undef;      # already on shelf
361         $query = qq(
362                 INSERT INTO virtualshelfcontents
363                         (shelfnumber, biblionumber, flags, borrowernumber)
364                 VALUES (?, ?, 0, ?));
365         $sth = $dbh->prepare($query);
366         $sth->execute( $shelfnumber, $biblionumber, $borrowernumber);
367         $query = qq(UPDATE virtualshelves
368                                 SET lastmodified = CURRENT_TIMESTAMP
369                                 WHERE shelfnumber = ?);
370         $sth = $dbh->prepare($query);
371         $sth->execute( $shelfnumber );
372 }
373
374 =head2 ModShelf
375
376 my $result= ModShelf($shelfnumber, $hashref)
377
378 Where $hashref->{column} = param
379
380 Modify the value into virtualshelves table with values given 
381 from hashref, which each key of the hashref should be
382 the name of a column of virtualshelves.
383 Fields like shelfnumber or owner cannot be changed.
384
385 Returns 1 if the action seemed to be successful.
386
387 =cut
388
389 sub ModShelf {
390     my ($shelfnumber,$hashref) = @_;
391
392     my $query= "SELECT * FROM virtualshelves WHERE shelfnumber=?";
393     my $sth = $dbh->prepare($query);
394     $sth->execute($shelfnumber);
395     my $oldrecord= $sth->fetchrow_hashref;
396     return 0 unless $oldrecord; #not found?
397
398     #initialize missing hash values to silence warnings
399     foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) {
400         $hashref->{$_}= exists $hashref->{$_}? $hashref->{$_}||'': '';
401     }
402
403     #if name or category changes, the name should be tested
404     if($hashref->{shelfname} || $hashref->{category}) {
405         unless(_CheckShelfName(
406             $hashref->{shelfname}||$oldrecord->{shelfname},
407             $hashref->{category}||$oldrecord->{category},
408             $oldrecord->{owner}, $shelfnumber )) {
409                 return 0; #name check failed
410         }
411     }
412
413     #only the following fields from the hash may be changed
414     $query= "UPDATE virtualshelves SET shelfname=?, category=?, sortfield=?, allow_add=?, allow_delete_own=?, allow_delete_other=? WHERE shelfnumber=?";
415     $sth = $dbh->prepare($query);
416     $sth->execute(
417         $hashref->{shelfname}||$oldrecord->{shelfname},
418         $hashref->{category}||$oldrecord->{category},
419         $hashref->{sortfield}||$oldrecord->{sortfield},
420         $hashref->{allow_add}||$oldrecord->{allow_add},
421         $hashref->{allow_delete_own}||$oldrecord->{allow_delete_own},
422         $hashref->{allow_delete_other}||$oldrecord->{allow_delete_other},
423         $shelfnumber );
424     return $@? 0: 1;
425 }
426
427 =head2 ShelfPossibleAction
428
429 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
430
431 C<$loggedinuser,$shelfnumber,$action>
432
433 $action can be "view", "add", "delete", "manage", "new_public", "new_private".
434 Note that add/delete here refers to adding/deleting entries from the list. Deleting the list itself falls under manage.
435 new_public and new_private refers to creating a new public or private list.
436 The distinction between deleting your own entries from the list or entries from
437 others is made in DelFromShelf.
438
439 Returns 1 if the user can do the $action in the $shelfnumber shelf.
440 Returns 0 otherwise.
441
442 =cut
443
444 sub ShelfPossibleAction {
445     my ( $user, $shelfnumber, $action ) = @_;
446     $action= 'view' unless $action;
447     $user=0 unless $user;
448
449     if($action =~ /^new/) { #no shelfnumber needed
450         if($action eq 'new_private') {
451             return $user>0;
452         }
453         elsif($action eq 'new_public') {
454             return $user>0 && C4::Context->preference('OpacAllowPublicListCreation');
455         }
456         return 0;
457     }
458
459     return 0 unless defined($shelfnumber);
460
461     my $query = qq/
462         SELECT IFNULL(owner,0) AS owner, category, allow_add, allow_delete_own, allow_delete_other, IFNULL(sh.borrowernumber,0) AS borrowernumber
463         FROM virtualshelves vs
464         LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
465         AND sh.borrowernumber=?
466         WHERE vs.shelfnumber=?
467     /;
468     my $sth = $dbh->prepare($query);
469     $sth->execute($user, $shelfnumber);
470     my $shelf= $sth->fetchrow_hashref;
471
472     return 0 unless $shelf && ($shelf->{category}==2 || $shelf->{owner}==$user || $shelf->{borrowernumber}==$user);
473     if($action eq 'view') {
474         #already handled in the above condition
475         return 1;
476     }
477     elsif($action eq 'add') {
478         return 0 if $user<=0; #should be logged in
479         return 1 if $shelf->{allow_add}==1 || $shelf->{owner}==$user;
480         #owner may always add
481     }
482     elsif($action eq 'delete') {
483         #this answer is just diplomatic: it says that you may be able to delete
484         #some items from that shelf
485         #it does not answer the question about a specific biblio
486         #DelFromShelf checks the situation per biblio
487         return 1 if $user>0 && ($shelf->{allow_delete_own}==1 || $shelf->{allow_delete_other}==1);
488     }
489     elsif($action eq 'manage') {
490         return 1 if $user && $shelf->{owner}==$user;
491     }
492     return 0;
493 }
494
495 =head2 DelFromShelf
496
497     $result= &DelFromShelf( $bibref, $shelfnumber, $user);
498
499 Removes biblionumbers in passed arrayref from shelf C<$shelfnumber>.
500 If the bib wasn't on that virtualshelves to begin with, nothing happens.
501
502 Returns 0 if no items have been deleted.
503
504 =cut
505
506 sub DelFromShelf {
507     my ($bibref, $shelfnumber, $user) = @_;
508     my $query = qq(SELECT allow_delete_own, allow_delete_other FROM virtualshelves WHERE shelfnumber=?);
509     my $sth= $dbh->prepare($query);
510     $sth->execute($shelfnumber);
511     my ($del_own, $del_oth)= $sth->fetchrow;
512     my $r; my $t=0;
513
514     if($del_own) {
515         $query = qq(DELETE FROM virtualshelfcontents
516             WHERE shelfnumber=? AND biblionumber=? AND borrowernumber=?);
517         $sth= $dbh->prepare($query);
518         foreach my $biblionumber (@$bibref) {
519             $sth->execute($shelfnumber, $biblionumber, $user);
520             $r= $sth->rows; #Expect -1, 0 or 1 (-1 means Don't know; count as 1)
521             $t+= ($r==-1)? 1: $r;
522         }
523     }
524     if($del_oth) {
525         $query = qq(DELETE FROM virtualshelfcontents
526             WHERE shelfnumber=? AND biblionumber=? AND borrowernumber<>?);
527         $sth= $dbh->prepare($query);
528         foreach my $biblionumber (@$bibref) {
529             $sth->execute($shelfnumber, $biblionumber, $user);
530             $r= $sth->rows;
531             $t+= ($r==-1)? 1: $r;
532         }
533     }
534     return $t;
535 }
536
537 =head2 DelShelf
538
539   $Number = DelShelf($shelfnumber);
540
541 This function deletes the shelf number, and all of it's content.
542 Authorization to do so MUST have been checked before calling, while using
543 ShelfPossibleAction with manage parameter.
544
545 =cut
546
547 sub DelShelf {
548     my ($shelfnumber)= @_;
549     return unless $shelfnumber && $shelfnumber =~ /^\d+$/;
550     my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?");
551     return $sth->execute($shelfnumber);
552 }
553
554 =head2 GetBibliosShelves
555
556 This finds all the public lists that this bib record is in.
557
558 =cut
559
560 sub GetBibliosShelves {
561     my ( $biblionumber )  = @_;
562     my $dbh = C4::Context->dbh;
563     my $sth = $dbh->prepare('
564         SELECT vs.shelfname, vs.shelfnumber 
565         FROM virtualshelves vs 
566         JOIN virtualshelfcontents vc ON (vs.shelfnumber= vc.shelfnumber) 
567         WHERE vs.category=2
568         AND vc.biblionumber= ?
569     ');
570     $sth->execute( $biblionumber );
571     return $sth->fetchall_arrayref({});
572 }
573
574 =head2 ShelvesMax
575
576     $howmany= ShelvesMax($context);
577
578 Tells how much shelves are shown in which context.
579 POPUP refers to addbybiblionumber popup, MGRPAGE is managing page (in opac or
580 staff), COMBO refers to the Add to-combo of search results. MASTHEAD is the
581 main Koha toolbar with Lists button.
582
583 =cut
584
585 sub ShelvesMax {
586   my $which= shift;
587   return SHELVES_POPUP_MAX if $which eq 'POPUP';
588   return SHELVES_MGRPAGE_MAX if $which eq 'MGRPAGE';
589   return SHELVES_COMBO_MAX if $which eq 'COMBO';
590   return SHELVES_MASTHEAD_MAX if $which eq 'MASTHEAD';
591   return SHELVES_MASTHEAD_MAX;
592 }
593
594 # internal subs
595
596 sub _shelf_count {
597     my ($owner, $category) = @_;
598     my @params;
599     # Find out how many shelves total meet the submitted criteria...
600
601     my $query = "SELECT count(*) FROM virtualshelves vs ";
602     if($category==1) {
603         $query.= qq{
604             LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
605             AND sh.borrowernumber=?
606             WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
607         @params= ($owner, $owner, $owner);
608     }
609     else {
610         $query.='WHERE category=2';
611         @params= ();
612     }
613     my $sth = $dbh->prepare($query);
614     $sth->execute(@params);
615     my ($total)= $sth->fetchrow;
616     return $total;
617 }
618
619 sub _biblionumber_sth { #only used in obsolete sub below
620     my ($shelf) = @_;
621     my $query = 'select biblionumber from virtualshelfcontents where shelfnumber = ?';
622     my $dbh = C4::Context->dbh;
623     my $sth = $dbh->prepare($query)
624         or die $dbh->errstr;
625     $sth->execute( $shelf )
626         or die $sth->errstr;
627     $sth;
628 }
629
630 sub each_biblionumbers (&$) { #OBSOLETE
631     my ($code,$shelf) = @_;
632     my $ref =  _biblionumber_sth($shelf)->fetchall_arrayref;
633     map {
634         $_=$$_[0];
635         $code->();
636     } @$ref;
637 }
638
639 sub _CheckShelfName {
640     my ($name, $cat, $owner, $number)= @_;
641
642     my $query = qq(
643         SELECT DISTINCT shelfnumber
644         FROM   virtualshelves
645         LEFT JOIN virtualshelfshares sh USING (shelfnumber)
646         WHERE  shelfname=? AND shelfnumber<>?);
647     if($cat==1) {
648         $query.= ' AND (sh.borrowernumber=? OR owner=?) AND category=1';
649     }
650     else {
651         $query.= ' AND category=2';
652     }
653     my $sth = $dbh->prepare($query);
654     $sth->execute($cat==1? ($name, $number, $owner, $owner): ($name, $number));
655     return $sth->rows>0? 0: 1;
656 }
657
658 1;
659
660 __END__
661
662 =head1 AUTHOR
663
664 Koha Development Team <http://koha-community.org/>
665
666 =head1 SEE ALSO
667
668 C4::Circulation::Circ2(3)
669
670 =cut