1 package C4::VirtualShelves;
3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 use constant SHELVES_MASTHEAD_MAX => 10; #number under Lists button in masthead
28 use constant SHELVES_COMBO_MAX => 10; #add to combo in search
29 use constant SHELVES_MGRPAGE_MAX => 20; #managing page
30 use constant SHELVES_POPUP_MAX => 40; #addbybiblio popup
32 use constant SHARE_INVITATION_EXPIRY_DAYS => 14; #two weeks to accept
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
37 # set the version for version checking
38 $VERSION = 3.07.00.049;
42 &GetShelves &GetShelfContents &GetShelf
46 &DelFromShelf &DelShelf
47 &GetBibliosShelves &AddShare
50 &GetAllShelves &ShelvesMax
57 C4::VirtualShelves - Functions for manipulating Koha virtual shelves
61 use C4::VirtualShelves;
65 This module provides functions for manipulating virtual shelves,
66 including creating and deleting virtual shelves, and adding and removing
67 bibs to and from virtual shelves.
73 ($shelflist, $totshelves) = &GetShelves($category, $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<$category> criteria. C<$category>,
78 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$category> == 1.
79 When C<$category> is 2, supply undef as argument for C<$owner>.
81 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.
83 C<$shelflist>is a reference-to-hash. The keys are the virtualshelves numbers (C<$shelfnumber>, above),
84 and the values (C<$shelfhash>, above) are themselves references-to-hash, with the following keys:
88 =item C<$shelfhash-E<gt>{shelfname}>
90 A string. The name of the shelf.
92 =item C<$shelfhash-E<gt>{count}>
94 The number of virtuals on that virtualshelves.
101 my ($category, $row_count, $offset, $owner) = @_;
103 my $total = _shelf_count($owner, $category);
104 my $dbh = C4::Context->dbh;
106 SELECT vs.shelfnumber, vs.shelfname,vs.owner,
107 bo.surname,bo.firstname,vs.category,vs.sortfield,
108 count(vc.biblionumber) as count
109 FROM virtualshelves vs
110 LEFT JOIN borrowers bo ON vs.owner=bo.borrowernumber
111 LEFT JOIN virtualshelfcontents vc USING (shelfnumber) };
114 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
115 AND sh.borrowernumber=?
116 WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
117 @params= ($owner, $owner, $owner, $offset||0, $row_count);
120 $query.= 'WHERE category=2 ';
121 @params= ($offset||0, $row_count);
124 GROUP BY vs.shelfnumber
125 ORDER BY vs.shelfname
128 my $sth2 = $dbh->prepare($query);
129 $sth2->execute(@params);
131 while( my ($shelfnumber, $shelfname, $owner, $surname, $firstname, $category, $sortfield, $count)= $sth2->fetchrow) {
132 $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
133 $shelflist{$shelfnumber}->{'count'} = $count;
134 $shelflist{$shelfnumber}->{'single'} = $count==1;
135 $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
136 $shelflist{$shelfnumber}->{'category'} = $category;
137 $shelflist{$shelfnumber}->{'owner'} = $owner;
138 $shelflist{$shelfnumber}->{'surname'} = $surname;
139 $shelflist{$shelfnumber}->{'firstname'} = $firstname;
141 return ( \%shelflist, $total );
146 $shelflist = GetAllShelves($category, $owner)
148 This function returns a reference to an array of hashrefs containing all shelves
149 sorted by the shelf name.
151 This function is intended to return a dataset reflecting all the shelves for
152 the submitted parameters.
157 my ($category,$owner,$adding_allowed) = @_;
159 my $dbh = C4::Context->dbh;
160 my $query = 'SELECT vs.* FROM virtualshelves vs ';
163 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
164 AND sh.borrowernumber=?
165 WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
166 @params = ($owner, $owner, $owner);
169 $query.='WHERE category=2 ';
172 $query.='AND (allow_add=1 OR owner=?) ' if $adding_allowed;
173 push @params, $owner if $adding_allowed;
174 $query.= 'ORDER BY shelfname ASC';
175 my $sth = $dbh->prepare( $query );
176 $sth->execute(@params);
177 return $sth->fetchall_arrayref({});
180 =head2 GetSomeShelfNames
182 Returns shelf names and numbers for Add to combo of search results and Lists button of OPAC header.
186 sub GetSomeShelfNames {
187 my ($owner, $purpose, $adding_allowed)= @_;
188 my ($bar, $pub, @params);
189 my $dbh = C4::Context->dbh;
191 my $bquery = 'SELECT vs.shelfnumber, vs.shelfname FROM virtualshelves vs ';
192 my $limit= ShelvesMax($purpose);
194 my $qry1= $bquery."WHERE vs.category=2 ";
195 $qry1.= "AND (allow_add=1 OR owner=?) " if $adding_allowed;
196 push @params, $owner||0 if $adding_allowed;
197 $qry1.= "ORDER BY vs.lastmodified DESC LIMIT $limit";
199 unless($adding_allowed && (!defined($owner) || $owner<=0)) {
200 #if adding items, user should be known
201 $pub= $dbh->selectall_arrayref($qry1,{Slice=>{}},@params);
205 my $qry2= $bquery. qq{
206 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber AND sh.borrowernumber=?
207 WHERE vs.category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
208 @params=($owner,$owner,$owner);
209 $qry2.= "AND (allow_add=1 OR owner=?) " if $adding_allowed;
210 push @params, $owner if $adding_allowed;
211 $qry2.= "ORDER BY vs.lastmodified DESC ";
212 $qry2.= "LIMIT $limit";
213 $bar= $dbh->selectall_arrayref($qry2,{Slice=>{}},@params);
216 return ( { bartotal => $bar? scalar @$bar: 0, pubtotal => $pub? scalar @$pub: 0}, $pub, $bar);
221 (shelfnumber,shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other) = &GetShelf($shelfnumber);
223 Returns the above-mentioned fields for passed virtual shelf number.
228 my ($shelfnumber) = @_;
229 my $dbh = C4::Context->dbh;
231 SELECT shelfnumber, shelfname, owner, category, sortfield,
232 allow_add, allow_delete_own, allow_delete_other
236 my $sth = $dbh->prepare($query);
237 $sth->execute($shelfnumber);
238 return $sth->fetchrow;
241 =head2 GetShelfContents
243 $biblist = &GetShelfContents($shelfnumber);
245 Looks up information about the contents of virtual virtualshelves number
246 C<$shelfnumber>. Sorted by a field in the biblio table. copyrightdate
249 Returns a reference-to-array, whose elements are references-to-hash,
250 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
252 Note: the notforloan status comes from the itemtype, and where it equals 0
253 it does not ensure that related items.notforloan status is likewise 0. The
254 caller has to check any items on their own, possibly with CanBookBeIssued
255 from C4::Circulation.
259 sub GetShelfContents {
260 my ($shelfnumber, $row_count, $offset, $sortfield, $sort_direction ) = @_;
261 my $dbh=C4::Context->dbh();
262 my $sth1 = $dbh->prepare("SELECT count(*) FROM virtualshelfcontents WHERE shelfnumber = ?");
263 $sth1->execute($shelfnumber);
264 my $total = $sth1->fetchrow;
266 my $sth2 = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
267 $sth2->execute($shelfnumber);
268 ($sortfield) = $sth2->fetchrow_array;
271 " SELECT DISTINCT vc.biblionumber, vc.shelfnumber, vc.dateadded, itemtypes.*,
272 biblio.*, biblioitems.itemtype, biblioitems.publicationyear as year, biblioitems.publishercode, biblioitems.place, biblioitems.size, biblioitems.pages
273 FROM virtualshelfcontents vc
274 JOIN biblio ON vc.biblionumber = biblio.biblionumber
275 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
276 LEFT JOIN items ON items.biblionumber=vc.biblionumber
277 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
278 WHERE vc.shelfnumber=? ";
279 my @params = ($shelfnumber);
281 $query .= " ORDER BY " . $dbh->quote_identifier( $sortfield );
282 $query .= " DESC " if ( $sort_direction eq 'desc' );
285 $query .= " LIMIT ?, ? ";
286 push (@params, ($offset ? $offset : 0));
287 push (@params, $row_count);
289 my $sth3 = $dbh->prepare($query);
290 $sth3->execute(@params);
291 return ($sth3->fetchall_arrayref({}), $total);
292 # Like the perldoc says,
293 # returns reference-to-array, where each element is reference-to-hash of the row:
294 # like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ]
295 # Suitable for use in TMPL_LOOP.
296 # See http://search.cpan.org/~timb/DBI-1.601/DBI.pm#fetchall_arrayref
297 # or newer, for your version of DBI.
302 $shelfnumber = &AddShelf($hashref, $owner);
304 Creates a new virtual shelf. Params passed in a hash like ModShelf.
306 Returns a code to know what's happen.
307 * -1 : if this virtualshelves already exists.
308 * $shelfnumber : if success.
313 my ($hashref, $owner)= @_;
314 my $dbh = C4::Context->dbh;
316 #initialize missing hash values to silence warnings
317 foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) {
318 $hashref->{$_}= undef unless exists $hashref->{$_};
321 return -1 unless _CheckShelfName($hashref->{shelfname}, $hashref->{category}, $owner, 0);
323 my $query = qq(INSERT INTO virtualshelves
324 (shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other)
325 VALUES (?,?,?,?,?,?,?));
327 my $sth = $dbh->prepare($query);
329 $hashref->{shelfname},
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'};
342 &AddToShelf($biblionumber, $shelfnumber, $borrower);
344 Adds bib number C<$biblionumber> to virtual virtualshelves number
345 C<$shelfnumber>, unless that bib is already on that shelf.
350 my ($biblionumber, $shelfnumber, $borrowernumber) = @_;
351 return unless $biblionumber;
352 my $dbh = C4::Context->dbh;
355 FROM virtualshelfcontents
356 WHERE shelfnumber=? AND biblionumber=?
358 my $sth = $dbh->prepare($query);
360 $sth->execute( $shelfnumber, $biblionumber );
361 ($sth->rows) and return; # already on shelf
363 INSERT INTO virtualshelfcontents
364 (shelfnumber, biblionumber, flags, borrowernumber)
365 VALUES (?, ?, 0, ?));
366 $sth = $dbh->prepare($query);
367 $sth->execute( $shelfnumber, $biblionumber, $borrowernumber);
368 $query = qq(UPDATE virtualshelves
369 SET lastmodified = CURRENT_TIMESTAMP
370 WHERE shelfnumber = ?);
371 $sth = $dbh->prepare($query);
372 $sth->execute( $shelfnumber );
377 my $result= ModShelf($shelfnumber, $hashref)
379 Where $hashref->{column} = param
381 Modify the value into virtualshelves table with values given
382 from hashref, which each key of the hashref should be
383 the name of a column of virtualshelves.
384 Fields like shelfnumber or owner cannot be changed.
386 Returns 1 if the action seemed to be successful.
391 my ($shelfnumber,$hashref) = @_;
392 my $dbh = C4::Context->dbh;
394 my $query= "SELECT * FROM virtualshelves WHERE shelfnumber=?";
395 my $sth = $dbh->prepare($query);
396 $sth->execute($shelfnumber);
397 my $oldrecord= $sth->fetchrow_hashref;
398 return 0 unless $oldrecord; #not found?
400 #initialize missing hash values to silence warnings
401 foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) {
402 $hashref->{$_}= undef unless exists $hashref->{$_};
405 #if name or category changes, the name should be tested
406 if($hashref->{shelfname} || $hashref->{category}) {
407 unless(_CheckShelfName(
408 $hashref->{shelfname}//$oldrecord->{shelfname},
409 $hashref->{category}//$oldrecord->{category},
412 return 0; #name check failed
416 #only the following fields from the hash may be changed
417 $query= "UPDATE virtualshelves SET shelfname=?, category=?, sortfield=?, allow_add=?, allow_delete_own=?, allow_delete_other=? WHERE shelfnumber=?";
418 $sth = $dbh->prepare($query);
420 $hashref->{shelfname}//$oldrecord->{shelfname},
421 $hashref->{category}//$oldrecord->{category},
422 $hashref->{sortfield}//$oldrecord->{sortfield},
423 $hashref->{allow_add}//$oldrecord->{allow_add},
424 $hashref->{allow_delete_own}//$oldrecord->{allow_delete_own},
425 $hashref->{allow_delete_other}//$oldrecord->{allow_delete_other},
430 =head2 ShelfPossibleAction
432 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
434 C<$loggedinuser,$shelfnumber,$action>
436 $action can be "view", "add", "delete", "manage", "new_public", "new_private".
437 Note that add/delete here refers to adding/deleting entries from the list. Deleting the list itself falls under manage.
438 new_public and new_private refers to creating a new public or private list.
439 The distinction between deleting your own entries from the list or entries from
440 others is made in DelFromShelf.
442 Returns 1 if the user can do the $action in the $shelfnumber shelf.
447 sub ShelfPossibleAction {
448 my ( $user, $shelfnumber, $action ) = @_;
449 $action= 'view' unless $action;
450 $user=0 unless $user;
452 if($action =~ /^new/) { #no shelfnumber needed
453 if($action eq 'new_private') {
456 elsif($action eq 'new_public') {
457 return $user>0 && C4::Context->preference('OpacAllowPublicListCreation');
462 return 0 unless defined($shelfnumber);
464 my $dbh = C4::Context->dbh;
466 SELECT COALESCE(owner,0) AS owner, category, allow_add, allow_delete_own, allow_delete_other, COALESCE(sh.borrowernumber,0) AS borrowernumber
467 FROM virtualshelves vs
468 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
469 AND sh.borrowernumber=?
470 WHERE vs.shelfnumber=?
472 my $sth = $dbh->prepare($query);
473 $sth->execute($user, $shelfnumber);
474 my $shelf= $sth->fetchrow_hashref;
476 return 0 unless $shelf && ($shelf->{category}==2 || $shelf->{owner}==$user || ($user && $shelf->{borrowernumber}==$user));
477 if($action eq 'view') {
478 #already handled in the above condition
481 elsif($action eq 'add') {
482 return 0 if $user<=0; #should be logged in
483 return 1 if $shelf->{allow_add}==1 || $shelf->{owner}==$user;
484 #owner may always add
486 elsif($action eq 'delete') {
487 #this answer is just diplomatic: it says that you may be able to delete
488 #some items from that shelf
489 #it does not answer the question about a specific biblio
490 #DelFromShelf checks the situation per biblio
491 return 1 if $user>0 && ($shelf->{allow_delete_own}==1 || $shelf->{allow_delete_other}==1);
493 elsif($action eq 'manage') {
494 return 1 if $user && $shelf->{owner}==$user;
501 $result= &DelFromShelf( $bibref, $shelfnumber, $user);
503 Removes biblionumbers in passed arrayref from shelf C<$shelfnumber>.
504 If the bib wasn't on that virtualshelves to begin with, nothing happens.
506 Returns 0 if no items have been deleted.
511 my ($bibref, $shelfnumber, $user) = @_;
512 my $dbh = C4::Context->dbh;
513 my $query = qq(SELECT allow_delete_own, allow_delete_other FROM virtualshelves WHERE shelfnumber=?);
514 my $sth= $dbh->prepare($query);
515 $sth->execute($shelfnumber);
516 my ($del_own, $del_oth)= $sth->fetchrow;
520 $query = qq(DELETE FROM virtualshelfcontents
521 WHERE shelfnumber=? AND biblionumber=? AND borrowernumber=?);
522 $sth= $dbh->prepare($query);
523 foreach my $biblionumber (@$bibref) {
524 $sth->execute($shelfnumber, $biblionumber, $user);
525 $r= $sth->rows; #Expect -1, 0 or 1 (-1 means Don't know; count as 1)
526 $t+= ($r==-1)? 1: $r;
530 #includes a check if borrowernumber is null (deleted patron)
531 $query = qq/DELETE FROM virtualshelfcontents
532 WHERE shelfnumber=? AND biblionumber=? AND
533 (borrowernumber IS NULL OR borrowernumber<>?)/;
534 $sth= $dbh->prepare($query);
535 foreach my $biblionumber (@$bibref) {
536 $sth->execute($shelfnumber, $biblionumber, $user);
538 $t+= ($r==-1)? 1: $r;
546 $Number = DelShelf($shelfnumber);
548 This function deletes the shelf number, and all of it's content.
549 Authorization to do so MUST have been checked before calling, while using
550 ShelfPossibleAction with manage parameter.
555 my ($shelfnumber)= @_;
556 return unless $shelfnumber && $shelfnumber =~ /^\d+$/;
557 my $dbh = C4::Context->dbh;
558 my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?");
559 return $sth->execute($shelfnumber);
562 =head2 GetBibliosShelves
564 This finds all the public lists that this bib record is in.
568 sub GetBibliosShelves {
569 my ( $biblionumber ) = @_;
570 my $dbh = C4::Context->dbh;
571 my $sth = $dbh->prepare('
572 SELECT vs.shelfname, vs.shelfnumber
573 FROM virtualshelves vs
574 JOIN virtualshelfcontents vc ON (vs.shelfnumber= vc.shelfnumber)
576 AND vc.biblionumber= ?
578 $sth->execute( $biblionumber );
579 return $sth->fetchall_arrayref({});
584 $howmany= ShelvesMax($context);
586 Tells how much shelves are shown in which context.
587 POPUP refers to addbybiblionumber popup, MGRPAGE is managing page (in opac or
588 staff), COMBO refers to the Add to-combo of search results. MASTHEAD is the
589 main Koha toolbar with Lists button.
595 return SHELVES_POPUP_MAX if $which eq 'POPUP';
596 return SHELVES_MGRPAGE_MAX if $which eq 'MGRPAGE';
597 return SHELVES_COMBO_MAX if $which eq 'COMBO';
598 return SHELVES_MASTHEAD_MAX if $which eq 'MASTHEAD';
599 return SHELVES_MASTHEAD_MAX;
602 =head2 HandleDelBorrower
604 HandleDelBorrower($borrower);
606 When a member is deleted (DelMember in Members.pm), you should call me first.
607 This routine deletes/moves lists and entries for the deleted member/borrower.
608 You could just delete everything (and lose more than you want), but instead we
609 now try to save all public/shared stuff and keep others happy.
613 sub HandleDelBorrower {
616 my $dbh = C4::Context->dbh;
618 #Delete shares of this borrower (not lists !)
619 #Although this would be done later via the FK cascaded delete, we do it now.
620 #Because it makes the following delete statement on shelves more meaningful.
621 $query="DELETE FROM virtualshelfshares WHERE borrowernumber=?";
622 $dbh->do($query,undef,($borrower));
624 #Delete private lists without owner that now have no shares anymore
625 $query="DELETE vs.* FROM virtualshelves vs LEFT JOIN virtualshelfshares sh USING (shelfnumber) WHERE category=1 AND vs.owner IS NULL AND sh.shelfnumber IS NULL";
628 #Change owner for private lists which have shares
629 $query="UPDATE virtualshelves LEFT JOIN virtualshelfshares sh USING (shelfnumber) SET owner=NULL where owner=? AND category=1 AND sh.borrowernumber IS NOT NULL";
630 $dbh->do($query,undef,($borrower));
632 #Delete unshared private lists
633 $query="DELETE FROM virtualshelves WHERE owner=? AND category=1";
634 $dbh->do($query,undef,($borrower));
636 #Handle public lists owned by borrower
637 $query="UPDATE virtualshelves SET owner=NULL WHERE owner=? AND category=2";
638 $dbh->do($query,undef,($borrower));
640 #Handle entries added by borrower to lists of others
641 $query="UPDATE virtualshelfcontents SET borrowernumber=NULL WHERE borrowernumber=?";
642 $dbh->do($query,undef,($borrower));
647 AddShare($shelfnumber, $key);
649 Adds a share request to the virtualshelves table.
650 Authorization must have been checked, and a key must be supplied. See script
651 opac-shareshelf.pl for an example.
652 This request is not yet confirmed. So it has no borrowernumber, it does have an
658 my ($shelfnumber, $key)= @_;
659 return if !$shelfnumber || !$key;
662 my $dbh = C4::Context->dbh;
663 $sql="DELETE FROM virtualshelfshares WHERE sharedate<NOW() LIMIT 10";
664 #housekeeping: add one, remove max 10 expired ones
666 $sql="INSERT INTO virtualshelfshares (shelfnumber, invitekey, sharedate) VALUES (?, ?, ADDDATE(NOW(),?))";
667 $dbh->do($sql, undef, ($shelfnumber, $key, SHARE_INVITATION_EXPIRY_DAYS));
673 my ($owner, $category) = @_;
675 # Find out how many shelves total meet the submitted criteria...
677 my $dbh = C4::Context->dbh;
678 my $query = "SELECT count(*) FROM virtualshelves vs ";
681 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
682 AND sh.borrowernumber=?
683 WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
684 @params= ($owner, $owner, $owner);
687 $query.='WHERE category=2';
690 my $sth = $dbh->prepare($query);
691 $sth->execute(@params);
692 my ($total)= $sth->fetchrow;
696 sub _CheckShelfName {
697 my ($name, $cat, $owner, $number)= @_;
699 my $dbh = C4::Context->dbh;
702 SELECT DISTINCT shelfnumber
704 LEFT JOIN virtualshelfshares sh USING (shelfnumber)
705 WHERE shelfname=? AND shelfnumber<>?);
706 if($cat==1 && defined($owner)) {
707 $query.= ' AND (sh.borrowernumber=? OR owner=?) AND category=1';
708 @pars=($name, $number, $owner, $owner);
710 elsif($cat==1 && !defined($owner)) { #owner is null (exceptional)
711 $query.= ' AND owner IS NULL AND category=1';
712 @pars=($name, $number);
715 $query.= ' AND category=2';
716 @pars=($name, $number);
718 my $sth = $dbh->prepare($query);
719 $sth->execute(@pars);
720 return $sth->rows>0? 0: 1;
729 Koha Development Team <http://koha-community.org/>
733 C4::Circulation::Circ2(3)