Bug 13417: Limit the permission to delete
[koha.git] / C4 / VirtualShelves.pm
blob8fd003a89c8a5205bac43412eeb595d97c60ab66
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
10 # version.
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.
20 use strict;
21 use warnings;
23 use Carp;
24 use C4::Auth;
25 use C4::Context;
26 use C4::Debug;
27 use C4::Members;
29 use constant SHELVES_MASTHEAD_MAX => 10; #number under Lists button in masthead
30 use constant SHELVES_COMBO_MAX => 10; #add to combo in search
31 use constant SHELVES_MGRPAGE_MAX => 20; #managing page
32 use constant SHELVES_POPUP_MAX => 40; #addbybiblio popup
34 use constant SHARE_INVITATION_EXPIRY_DAYS => 14; #two weeks to accept
36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
38 BEGIN {
39 # set the version for version checking
40 $VERSION = 3.07.00.049;
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 &AddShare &AcceptShare &RemoveShare &IsSharedList
52 @EXPORT_OK = qw(
53 &GetAllShelves &ShelvesMax
58 =head1 NAME
60 C4::VirtualShelves - Functions for manipulating Koha virtual shelves
62 =head1 SYNOPSIS
64 use C4::VirtualShelves;
66 =head1 DESCRIPTION
68 This module provides functions for manipulating virtual shelves,
69 including creating and deleting virtual shelves, and adding and removing
70 bibs to and from virtual shelves.
72 =head1 FUNCTIONS
74 =head2 GetShelves
76 $shelflist = &GetShelves($category, $row_count, $offset, $owner);
77 ($shelfnumber, $shelfhash) = each %{$shelflist};
79 Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
80 number of shelves that meet the C<$owner> and C<$category> criteria. C<$category>,
81 C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$category> == 1.
82 When C<$category> is 2, supply undef as argument for C<$owner>.
84 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 C<$shelflist>is a reference-to-hash. The keys are the virtualshelves numbers (C<$shelfnumber>, above),
87 and the values (C<$shelfhash>, above) are themselves references-to-hash, with the following keys:
89 =over
91 =item C<$shelfhash-E<gt>{shelfname}>
93 A string. The name of the shelf.
95 =back
97 =cut
99 sub GetShelves {
100 my ($category, $row_count, $offset, $owner) = @_;
101 $offset ||= 0;
102 my @params = ( $offset, $row_count );
103 my $dbh = C4::Context->dbh;
104 my $query = qq{
105 SELECT vs.shelfnumber, vs.shelfname,vs.owner,
106 bo.surname,bo.firstname,vs.category,vs.sortfield,
107 count(vc.biblionumber) as count
108 FROM virtualshelves vs
109 LEFT JOIN borrowers bo ON vs.owner=bo.borrowernumber
110 LEFT JOIN virtualshelfcontents vc USING (shelfnumber) };
111 if($category==1) {
112 $query.= qq{
113 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
114 AND sh.borrowernumber=?
115 WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
116 unshift @params, ($owner) x 3;
118 else {
119 $query.= 'WHERE category=2 ';
121 $query.= qq{
122 GROUP BY vs.shelfnumber
123 ORDER BY vs.shelfname
124 LIMIT ?, ?};
126 my $sth2 = $dbh->prepare($query);
127 $sth2->execute(@params);
128 my %shelflist;
129 while( my ($shelfnumber, $shelfname, $owner, $surname, $firstname, $category, $sortfield, $count)= $sth2->fetchrow) {
130 $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
131 $shelflist{$shelfnumber}->{'count'} = $count;
132 $shelflist{$shelfnumber}->{'single'} = $count==1;
133 $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
134 $shelflist{$shelfnumber}->{'category'} = $category;
135 $shelflist{$shelfnumber}->{'owner'} = $owner;
136 $shelflist{$shelfnumber}->{'surname'} = $surname;
137 $shelflist{$shelfnumber}->{'firstname'} = $firstname;
139 return \%shelflist;
142 =head2 GetAllShelves
144 $shelflist = GetAllShelves($category, $owner)
146 This function returns a reference to an array of hashrefs containing all shelves
147 sorted by the shelf name.
149 This function is intended to return a dataset reflecting all the shelves for
150 the submitted parameters.
152 =cut
154 sub GetAllShelves {
155 my ($category,$owner,$adding_allowed) = @_;
156 my @params;
157 my $dbh = C4::Context->dbh;
158 my $query = 'SELECT vs.* FROM virtualshelves vs ';
159 if($category==1) {
160 $query.= qq{
161 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
162 AND sh.borrowernumber=?
163 WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
164 @params = ($owner, $owner, $owner);
166 else {
167 $query.='WHERE category=2 ';
168 @params = ();
170 $query.='AND (allow_add=1 OR owner=?) ' if $adding_allowed;
171 push @params, $owner if $adding_allowed;
172 $query.= 'ORDER BY shelfname ASC';
173 my $sth = $dbh->prepare( $query );
174 $sth->execute(@params);
175 return $sth->fetchall_arrayref({});
178 =head2 GetSomeShelfNames
180 Returns shelf names and numbers for Add to combo of search results and Lists button of OPAC header.
182 =cut
184 sub GetSomeShelfNames {
185 my ($owner, $purpose, $adding_allowed)= @_;
186 my ($bar, $pub, @params);
187 my $dbh = C4::Context->dbh;
189 my $bquery = 'SELECT vs.shelfnumber, vs.shelfname FROM virtualshelves vs ';
190 my $limit= ShelvesMax($purpose);
192 my $qry1= $bquery."WHERE vs.category=2 ";
193 $qry1.= "AND (allow_add=1 OR owner=?) " if $adding_allowed;
194 push @params, $owner||0 if $adding_allowed;
195 $qry1.= "ORDER BY vs.lastmodified DESC LIMIT $limit";
197 unless($adding_allowed && (!defined($owner) || $owner<=0)) {
198 #if adding items, user should be known
199 $pub= $dbh->selectall_arrayref($qry1,{Slice=>{}},@params);
202 if($owner) {
203 my $qry2= $bquery. qq{
204 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber AND sh.borrowernumber=?
205 WHERE vs.category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
206 @params=($owner,$owner,$owner);
207 $qry2.= "AND (allow_add=1 OR owner=?) " if $adding_allowed;
208 push @params, $owner if $adding_allowed;
209 $qry2.= "ORDER BY vs.lastmodified DESC ";
210 $qry2.= "LIMIT $limit";
211 $bar= $dbh->selectall_arrayref($qry2,{Slice=>{}},@params);
214 return ( { bartotal => $bar? scalar @$bar: 0, pubtotal => $pub? scalar @$pub: 0}, $pub, $bar);
217 =head2 GetShelf
219 (shelfnumber,shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other) = &GetShelf($shelfnumber);
221 Returns the above-mentioned fields for passed virtual shelf number.
223 =cut
225 sub GetShelf {
226 my ($shelfnumber) = @_;
227 my $dbh = C4::Context->dbh;
228 my $query = qq(
229 SELECT shelfnumber, shelfname, owner, category, sortfield,
230 allow_add, allow_delete_own, allow_delete_other
231 FROM virtualshelves
232 WHERE shelfnumber=?
234 my $sth = $dbh->prepare($query);
235 $sth->execute($shelfnumber);
236 return $sth->fetchrow;
239 =head2 GetShelfContents
241 $biblist = &GetShelfContents($shelfnumber);
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.
247 Returns a reference-to-array, whose elements are references-to-hash,
248 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
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.
255 =cut
257 sub GetShelfContents {
258 my ($shelfnumber, $row_count, $offset, $sortfield, $sort_direction ) = @_;
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;
268 my $query =
269 " SELECT DISTINCT vc.biblionumber, vc.shelfnumber, vc.dateadded, itemtypes.*,
270 biblio.*, biblioitems.itemtype, biblioitems.publicationyear as year, biblioitems.publishercode, biblioitems.place, biblioitems.size, biblioitems.pages
271 FROM virtualshelfcontents vc
272 JOIN biblio ON vc.biblionumber = biblio.biblionumber
273 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
274 LEFT JOIN items ON items.biblionumber=vc.biblionumber
275 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
276 WHERE vc.shelfnumber=? ";
277 my @params = ($shelfnumber);
278 if($sortfield) {
279 $query .= " ORDER BY " . $dbh->quote_identifier( $sortfield );
280 $query .= " DESC " if ( $sort_direction eq 'desc' );
282 if($row_count){
283 $query .= " LIMIT ?, ? ";
284 push (@params, ($offset ? $offset : 0));
285 push (@params, $row_count);
287 my $sth3 = $dbh->prepare($query);
288 $sth3->execute(@params);
289 return ($sth3->fetchall_arrayref({}), $total);
290 # Like the perldoc says,
291 # returns reference-to-array, where each element is reference-to-hash of the row:
292 # like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ]
293 # Suitable for use in TMPL_LOOP.
294 # See http://search.cpan.org/~timb/DBI-1.601/DBI.pm#fetchall_arrayref
295 # or newer, for your version of DBI.
298 =head2 AddShelf
300 $shelfnumber = &AddShelf($hashref, $owner);
302 Creates a new virtual shelf. Params passed in a hash like ModShelf.
304 Returns a code to know what's happen.
305 * -1 : if this virtualshelves already exists.
306 * $shelfnumber : if success.
308 =cut
310 sub AddShelf {
311 my ($hashref, $owner)= @_;
312 my $dbh = C4::Context->dbh;
314 #initialize missing hash values to silence warnings
315 foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) {
316 $hashref->{$_}= undef unless exists $hashref->{$_};
319 return -1 unless _CheckShelfName($hashref->{shelfname}, $hashref->{category}, $owner, 0);
321 my $query = qq(INSERT INTO virtualshelves
322 (shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other)
323 VALUES (?,?,?,?,?,?,?));
325 my $sth = $dbh->prepare($query);
326 $sth->execute(
327 $hashref->{shelfname},
328 $owner,
329 $hashref->{category},
330 $hashref->{sortfield},
331 $hashref->{allow_add}//0,
332 $hashref->{allow_delete_own}//1,
333 $hashref->{allow_delete_other}//0 );
334 return if $sth->err;
335 my $shelfnumber = $dbh->{'mysql_insertid'};
336 return $shelfnumber;
339 =head2 AddToShelf
341 &AddToShelf($biblionumber, $shelfnumber, $borrower);
343 Adds bib number C<$biblionumber> to virtual virtualshelves number
344 C<$shelfnumber>, unless that bib is already on that shelf.
346 =cut
348 sub AddToShelf {
349 my ($biblionumber, $shelfnumber, $borrowernumber) = @_;
350 return unless $biblionumber;
351 my $dbh = C4::Context->dbh;
352 my $query = qq(
353 SELECT *
354 FROM virtualshelfcontents
355 WHERE shelfnumber=? AND biblionumber=?
357 my $sth = $dbh->prepare($query);
359 $sth->execute( $shelfnumber, $biblionumber );
360 ($sth->rows) and return; # 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 );
374 =head2 ModShelf
376 my $result= ModShelf($shelfnumber, $hashref)
378 Where $hashref->{column} = param
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.
385 Returns 1 if the action seemed to be successful.
387 =cut
389 sub ModShelf {
390 my ($shelfnumber,$hashref) = @_;
391 my $dbh = C4::Context->dbh;
393 my $query= "SELECT * FROM virtualshelves WHERE shelfnumber=?";
394 my $sth = $dbh->prepare($query);
395 $sth->execute($shelfnumber);
396 my $oldrecord= $sth->fetchrow_hashref;
397 return 0 unless $oldrecord; #not found?
399 #initialize missing hash values to silence warnings
400 foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) {
401 $hashref->{$_}= undef unless exists $hashref->{$_};
404 #if name or category changes, the name should be tested
405 if($hashref->{shelfname} || $hashref->{category}) {
406 unless(_CheckShelfName(
407 $hashref->{shelfname}//$oldrecord->{shelfname},
408 $hashref->{category}//$oldrecord->{category},
409 $oldrecord->{owner},
410 $shelfnumber )) {
411 return 0; #name check failed
415 #only the following fields from the hash may be changed
416 $query= "UPDATE virtualshelves SET shelfname=?, category=?, sortfield=?, allow_add=?, allow_delete_own=?, allow_delete_other=? WHERE shelfnumber=?";
417 $sth = $dbh->prepare($query);
418 $sth->execute(
419 $hashref->{shelfname}//$oldrecord->{shelfname},
420 $hashref->{category}//$oldrecord->{category},
421 $hashref->{sortfield}//$oldrecord->{sortfield},
422 $hashref->{allow_add}//$oldrecord->{allow_add},
423 $hashref->{allow_delete_own}//$oldrecord->{allow_delete_own},
424 $hashref->{allow_delete_other}//$oldrecord->{allow_delete_other},
425 $shelfnumber );
426 return $@? 0: 1;
429 =head2 ShelfPossibleAction
431 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
433 C<$loggedinuser,$shelfnumber,$action>
435 $action can be "view", "add", "delete", "manage", "new_public", "new_private".
436 New additional actions are: invite, acceptshare.
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.
443 Returns 0 otherwise.
444 For the actions invite and acceptshare a second errorcode is returned if the
445 result is false. See opac-shareshelf.pl
447 =cut
449 sub ShelfPossibleAction {
450 my ( $user, $shelfnumber, $action ) = @_;
451 $action= 'view' unless $action;
452 $user=0 unless $user;
454 if($action =~ /^new/) { #no shelfnumber needed
455 if($action eq 'new_private') {
456 return $user>0;
458 elsif($action eq 'new_public') {
459 return $user>0 && C4::Context->preference('OpacAllowPublicListCreation');
461 return 0;
464 return 0 unless defined($shelfnumber);
466 if ( $user > 0 and $action eq 'delete_shelf' ) {
467 my $borrower = C4::Members::GetMember( borrowernumber => $user );
468 return 1
469 if C4::Auth::haspermission( $borrower->{userid}, { shelves => 'delete_shelves' } );
472 my $dbh = C4::Context->dbh;
473 my $query = qq/
474 SELECT COALESCE(owner,0) AS owner, category, allow_add, allow_delete_own, allow_delete_other, COALESCE(sh.borrowernumber,0) AS borrowernumber
475 FROM virtualshelves vs
476 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
477 AND sh.borrowernumber=?
478 WHERE vs.shelfnumber=?
480 my $sth = $dbh->prepare($query);
481 $sth->execute($user, $shelfnumber);
482 my $shelf= $sth->fetchrow_hashref;
484 return 0 unless $shelf && ($shelf->{category}==2 || $shelf->{owner}==$user || ($user && $shelf->{borrowernumber}==$user));
485 if($action eq 'view') {
486 #already handled in the above condition
487 return 1;
489 elsif($action eq 'add') {
490 return 0 if $user<=0; #should be logged in
491 return 1 if $shelf->{allow_add}==1 || $shelf->{owner}==$user;
492 #owner may always add
494 elsif($action eq 'delete') {
495 #this answer is just diplomatic: it says that you may be able to delete
496 #some items from that shelf
497 #it does not answer the question about a specific biblio
498 #DelFromShelf checks the situation per biblio
499 return 1 if $user>0 && ($shelf->{allow_delete_own}==1 || $shelf->{allow_delete_other}==1);
501 elsif($action eq 'invite') {
502 #for sharing you must be the owner and the list must be private
503 if( $shelf->{category}==1 ) {
504 return 1 if $shelf->{owner}==$user;
505 return (0, 4); # code 4: should be owner
507 else {
508 return (0, 5); # code 5: should be private list
511 elsif($action eq 'acceptshare') {
512 #the key for accepting is checked later in AcceptShare
513 #you must not be the owner, list must be private
514 if( $shelf->{category}==1 ) {
515 return (0, 8) if $shelf->{owner}==$user;
516 #code 8: should not be owner
517 return 1;
519 else {
520 return (0, 5); # code 5: should be private list
523 elsif($action eq 'manage' or $action eq 'delete_shelf') {
524 return 1 if $user && $shelf->{owner}==$user;
526 return 0;
529 =head2 DelFromShelf
531 $result= &DelFromShelf( $bibref, $shelfnumber, $user);
533 Removes biblionumbers in passed arrayref from shelf C<$shelfnumber>.
534 If the bib wasn't on that virtualshelves to begin with, nothing happens.
536 Returns 0 if no items have been deleted.
538 =cut
540 sub DelFromShelf {
541 my ($bibref, $shelfnumber, $user) = @_;
542 my $dbh = C4::Context->dbh;
543 my $query = qq(SELECT allow_delete_own, allow_delete_other FROM virtualshelves WHERE shelfnumber=?);
544 my $sth= $dbh->prepare($query);
545 $sth->execute($shelfnumber);
546 my ($del_own, $del_oth)= $sth->fetchrow;
547 my $r; my $t=0;
549 if($del_own) {
550 $query = qq(DELETE FROM virtualshelfcontents
551 WHERE shelfnumber=? AND biblionumber=? AND borrowernumber=?);
552 $sth= $dbh->prepare($query);
553 foreach my $biblionumber (@$bibref) {
554 $sth->execute($shelfnumber, $biblionumber, $user);
555 $r= $sth->rows; #Expect -1, 0 or 1 (-1 means Don't know; count as 1)
556 $t+= ($r==-1)? 1: $r;
559 if($del_oth) {
560 #includes a check if borrowernumber is null (deleted patron)
561 $query = qq/DELETE FROM virtualshelfcontents
562 WHERE shelfnumber=? AND biblionumber=? AND
563 (borrowernumber IS NULL OR borrowernumber<>?)/;
564 $sth= $dbh->prepare($query);
565 foreach my $biblionumber (@$bibref) {
566 $sth->execute($shelfnumber, $biblionumber, $user);
567 $r= $sth->rows;
568 $t+= ($r==-1)? 1: $r;
571 return $t;
574 =head2 DelShelf
576 $Number = DelShelf($shelfnumber);
578 This function deletes the shelf number, and all of it's content.
579 Authorization to do so MUST have been checked before calling, while using
580 ShelfPossibleAction with manage parameter.
582 =cut
584 sub DelShelf {
585 my ($shelfnumber)= @_;
586 return unless $shelfnumber && $shelfnumber =~ /^\d+$/;
587 my $dbh = C4::Context->dbh;
588 my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?");
589 return $sth->execute($shelfnumber);
592 =head2 GetBibliosShelves
594 This finds all the public lists that this bib record is in.
596 =cut
598 sub GetBibliosShelves {
599 my ( $biblionumber ) = @_;
600 my $dbh = C4::Context->dbh;
601 my $sth = $dbh->prepare('
602 SELECT vs.shelfname, vs.shelfnumber
603 FROM virtualshelves vs
604 JOIN virtualshelfcontents vc ON (vs.shelfnumber= vc.shelfnumber)
605 WHERE vs.category=2
606 AND vc.biblionumber= ?
608 $sth->execute( $biblionumber );
609 return $sth->fetchall_arrayref({});
612 =head2 ShelvesMax
614 $howmany= ShelvesMax($context);
616 Tells how much shelves are shown in which context.
617 POPUP refers to addbybiblionumber popup, MGRPAGE is managing page (in opac or
618 staff), COMBO refers to the Add to-combo of search results. MASTHEAD is the
619 main Koha toolbar with Lists button.
621 =cut
623 sub ShelvesMax {
624 my $which= shift;
625 return SHELVES_POPUP_MAX if $which eq 'POPUP';
626 return SHELVES_MGRPAGE_MAX if $which eq 'MGRPAGE';
627 return SHELVES_COMBO_MAX if $which eq 'COMBO';
628 return SHELVES_MASTHEAD_MAX if $which eq 'MASTHEAD';
629 return SHELVES_MASTHEAD_MAX;
632 =head2 HandleDelBorrower
634 HandleDelBorrower($borrower);
636 When a member is deleted (DelMember in Members.pm), you should call me first.
637 This routine deletes/moves lists and entries for the deleted member/borrower.
638 Lists owned by the borrower are deleted, but entries from the borrower to
639 other lists are kept.
641 =cut
643 sub HandleDelBorrower {
644 my ($borrower)= @_;
645 my $query;
646 my $dbh = C4::Context->dbh;
648 #Delete all lists and all shares of this borrower
649 #Consistent with the approach Koha uses on deleting individual lists
650 #Note that entries in virtualshelfcontents added by this borrower to
651 #lists of others will be handled by a table constraint: the borrower
652 #is set to NULL in those entries.
653 $query="DELETE FROM virtualshelves WHERE owner=?";
654 $dbh->do($query,undef,($borrower));
656 #NOTE:
657 #We could handle the above deletes via a constraint too.
658 #But a new BZ report 11889 has been opened to discuss another approach.
659 #Instead of deleting we could also disown lists (based on a pref).
660 #In that way we could save shared and public lists.
661 #The current table constraints support that idea now.
662 #This pref should then govern the results of other routines such as
663 #DelShelf too.
666 =head2 AddShare
668 AddShare($shelfnumber, $key);
670 Adds a share request to the virtualshelves table.
671 Authorization must have been checked, and a key must be supplied. See script
672 opac-shareshelf.pl for an example.
673 This request is not yet confirmed. So it has no borrowernumber, it does have an
674 expiry date.
676 =cut
678 sub AddShare {
679 my ($shelfnumber, $key)= @_;
680 return if !$shelfnumber || !$key;
682 my $dbh = C4::Context->dbh;
683 my $sql = "INSERT INTO virtualshelfshares (shelfnumber, invitekey, sharedate) VALUES (?, ?, NOW())";
684 $dbh->do($sql, undef, ($shelfnumber, $key));
685 return !$dbh->err;
688 =head2 AcceptShare
690 my $result= AcceptShare($shelfnumber, $key, $borrowernumber);
692 Checks acceptation of a share request.
693 Key must be found for this shelf. Invitation must not have expired.
694 Returns true when accepted, false otherwise.
696 =cut
698 sub AcceptShare {
699 my ($shelfnumber, $key, $borrowernumber)= @_;
700 return if !$shelfnumber || !$key || !$borrowernumber;
702 my $sql;
703 my $dbh = C4::Context->dbh;
704 $sql="
705 UPDATE virtualshelfshares
706 SET invitekey=NULL, sharedate=NOW(), borrowernumber=?
707 WHERE shelfnumber=? AND invitekey=? AND (sharedate + INTERVAL ? DAY) >NOW()
709 my $i= $dbh->do($sql, undef, ($borrowernumber, $shelfnumber, $key, SHARE_INVITATION_EXPIRY_DAYS));
710 return if !defined($i) || !$i || $i eq '0E0'; #not found
711 return 1;
714 =head2 IsSharedList
716 my $bool= IsSharedList( $shelfnumber );
718 IsSharedList checks if a (private) list has shares.
719 Note that such a check would not be useful for public lists. A public list has
720 no shares, but is visible for anyone by nature..
721 Used to determine the list type in the display of Your lists (all private).
722 Returns boolean value.
724 =cut
726 sub IsSharedList {
727 my ($shelfnumber) = @_;
728 my $dbh = C4::Context->dbh;
729 my $sql="SELECT id FROM virtualshelfshares WHERE shelfnumber=? AND borrowernumber IS NOT NULL";
730 my $sth = $dbh->prepare($sql);
731 $sth->execute($shelfnumber);
732 my ($rv)= $sth->fetchrow_array;
733 return defined($rv);
736 =head2 RemoveShare
738 RemoveShare( $user, $shelfnumber );
740 RemoveShare removes a share for specific shelf and borrower.
741 Returns true if a record could be deleted.
743 =cut
745 sub RemoveShare {
746 my ($user, $shelfnumber)= @_;
747 my $dbh = C4::Context->dbh;
748 my $sql="
749 DELETE FROM virtualshelfshares
750 WHERE borrowernumber=? AND shelfnumber=?
752 my $n= $dbh->do($sql,undef,($user, $shelfnumber));
753 return if !defined($n) || !$n || $n eq '0E0'; #nothing removed
754 return 1;
758 sub GetShelfCount {
759 my ($owner, $category) = @_;
760 my @params;
761 # Find out how many shelves total meet the submitted criteria...
763 my $dbh = C4::Context->dbh;
764 my $query = "SELECT count(*) FROM virtualshelves vs ";
765 if($category==1) {
766 $query.= qq{
767 LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber
768 AND sh.borrowernumber=?
769 WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) };
770 @params= ($owner, $owner, $owner);
772 else {
773 $query.='WHERE category=2';
774 @params= ();
776 my $sth = $dbh->prepare($query);
777 $sth->execute(@params);
778 my ($total)= $sth->fetchrow;
779 return $total;
782 # internal subs
783 sub _CheckShelfName {
784 my ($name, $cat, $owner, $number)= @_;
786 my $dbh = C4::Context->dbh;
787 my @pars;
788 my $query = qq(
789 SELECT DISTINCT shelfnumber
790 FROM virtualshelves
791 LEFT JOIN virtualshelfshares sh USING (shelfnumber)
792 WHERE shelfname=? AND shelfnumber<>?);
793 if($cat==1 && defined($owner)) {
794 $query.= ' AND (sh.borrowernumber=? OR owner=?) AND category=1';
795 @pars=($name, $number, $owner, $owner);
797 elsif($cat==1 && !defined($owner)) { #owner is null (exceptional)
798 $query.= ' AND owner IS NULL AND category=1';
799 @pars=($name, $number);
801 else { #public list
802 $query.= ' AND category=2';
803 @pars=($name, $number);
805 my $sth = $dbh->prepare($query);
806 $sth->execute(@pars);
807 return $sth->rows>0? 0: 1;
812 __END__
814 =head1 AUTHOR
816 Koha Development Team <http://koha-community.org/>
818 =head1 SEE ALSO
820 C4::Circulation::Circ2(3)
822 =cut