Bug 9032: add ability to invite another to share a private list
[koha.git] / C4 / VirtualShelves.pm
1 package C4::VirtualShelves;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
15 #
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.
19
20 use strict;
21 use warnings;
22
23 use Carp;
24 use C4::Context;
25 use C4::Debug;
26
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
31
32 use constant SHARE_INVITATION_EXPIRY_DAYS => 14; #two weeks to accept
33
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
35
36 BEGIN {
37     # set the version for version checking
38     $VERSION = 3.07.00.049;
39     require Exporter;
40     @ISA    = qw(Exporter);
41     @EXPORT = qw(
42             &GetShelves &GetShelfContents &GetShelf
43             &AddToShelf &AddShelf
44             &ModShelf
45             &ShelfPossibleAction
46             &DelFromShelf &DelShelf
47             &GetBibliosShelves &AddShare
48     );
49         @EXPORT_OK = qw(
50             &GetAllShelves &ShelvesMax
51         );
52 }
53
54
55 =head1 NAME
56
57 C4::VirtualShelves - Functions for manipulating Koha virtual shelves
58
59 =head1 SYNOPSIS
60
61   use C4::VirtualShelves;
62
63 =head1 DESCRIPTION
64
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.
68
69 =head1 FUNCTIONS
70
71 =head2 GetShelves
72
73   ($shelflist, $totshelves) = &GetShelves($category, $row_count, $offset, $owner);
74   ($shelfnumber, $shelfhash) = each %{$shelflist};
75
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>.
80
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.
82
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:
85
86 =over
87
88 =item C<$shelfhash-E<gt>{shelfname}>
89
90 A string. The name of the shelf.
91
92 =item C<$shelfhash-E<gt>{count}>
93
94 The number of virtuals on that virtualshelves.
95
96 =back
97
98 =cut
99
100 sub GetShelves {
101     my ($category, $row_count, $offset, $owner) = @_;
102     my @params;
103     my $total = _shelf_count($owner, $category);
104     my $dbh = C4::Context->dbh;
105     my $query = qq{
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) };
112     if($category==1) {
113         $query.= qq{
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);
118     }
119     else {
120         $query.= 'WHERE category=2 ';
121         @params= ($offset||0, $row_count);
122     }
123     $query.= qq{
124         GROUP BY vs.shelfnumber
125         ORDER BY vs.shelfname
126         LIMIT ?, ?};
127
128     my $sth2 = $dbh->prepare($query);
129     $sth2->execute(@params);
130     my %shelflist;
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;
140     }
141     return ( \%shelflist, $total );
142 }
143
144 =head2 GetAllShelves
145
146     $shelflist = GetAllShelves($category, $owner)
147
148 This function returns a reference to an array of hashrefs containing all shelves
149 sorted by the shelf name.
150
151 This function is intended to return a dataset reflecting all the shelves for
152 the submitted parameters.
153
154 =cut
155
156 sub GetAllShelves {
157     my ($category,$owner,$adding_allowed) = @_;
158     my @params;
159     my $dbh = C4::Context->dbh;
160     my $query = 'SELECT vs.* FROM virtualshelves vs ';
161     if($category==1) {
162         $query.= qq{
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);
167     }
168     else {
169     $query.='WHERE category=2 ';
170         @params = ();
171     }
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({});
178 }
179
180 =head2 GetSomeShelfNames
181
182 Returns shelf names and numbers for Add to combo of search results and Lists button of OPAC header.
183
184 =cut
185
186 sub GetSomeShelfNames {
187     my ($owner, $purpose, $adding_allowed)= @_;
188     my ($bar, $pub, @params);
189     my $dbh = C4::Context->dbh;
190
191     my $bquery = 'SELECT vs.shelfnumber, vs.shelfname FROM virtualshelves vs ';
192     my $limit= ShelvesMax($purpose);
193
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";
198
199     unless($adding_allowed && (!defined($owner) || $owner<=0)) {
200         #if adding items, user should be known
201         $pub= $dbh->selectall_arrayref($qry1,{Slice=>{}},@params);
202     }
203
204     if($owner) {
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);
214     }
215
216     return ( { bartotal => $bar? scalar @$bar: 0, pubtotal => $pub? scalar @$pub: 0}, $pub, $bar);
217 }
218
219 =head2 GetShelf
220
221   (shelfnumber,shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other) = &GetShelf($shelfnumber);
222
223 Returns the above-mentioned fields for passed virtual shelf number.
224
225 =cut
226
227 sub GetShelf {
228     my ($shelfnumber) = @_;
229     my $dbh = C4::Context->dbh;
230     my $query = qq(
231         SELECT shelfnumber, shelfname, owner, category, sortfield,
232             allow_add, allow_delete_own, allow_delete_other
233         FROM   virtualshelves
234         WHERE  shelfnumber=?
235     );
236     my $sth = $dbh->prepare($query);
237     $sth->execute($shelfnumber);
238     return $sth->fetchrow;
239 }
240
241 =head2 GetShelfContents
242
243   $biblist = &GetShelfContents($shelfnumber);
244
245 Looks up information about the contents of virtual virtualshelves number
246 C<$shelfnumber>.  Sorted by a field in the biblio table.  copyrightdate 
247 gives a desc sort.
248
249 Returns a reference-to-array, whose elements are references-to-hash,
250 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
251
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.
256
257 =cut
258
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;
265     if(!$sortfield) {
266         my $sth2 = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
267         $sth2->execute($shelfnumber);
268         ($sortfield) = $sth2->fetchrow_array;
269     }
270     my $query =
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);
280     if($sortfield) {
281         $query .= " ORDER BY " . $dbh->quote_identifier( $sortfield );
282         $query .= " DESC " if ( $sort_direction eq 'desc' );
283     }
284     if($row_count){
285        $query .= " LIMIT ?, ? ";
286        push (@params, ($offset ? $offset : 0));
287        push (@params, $row_count);
288     }
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.
298 }
299
300 =head2 AddShelf
301
302   $shelfnumber = &AddShelf($hashref, $owner);
303
304 Creates a new virtual shelf. Params passed in a hash like ModShelf.
305
306 Returns a code to know what's happen.
307     * -1 : if this virtualshelves already exists.
308     * $shelfnumber : if success.
309
310 =cut
311
312 sub AddShelf {
313     my ($hashref, $owner)= @_;
314     my $dbh = C4::Context->dbh;
315
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->{$_};
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 $dbh = C4::Context->dbh;
353     my $query = qq(
354         SELECT *
355         FROM   virtualshelfcontents
356         WHERE  shelfnumber=? AND biblionumber=?
357     );
358     my $sth = $dbh->prepare($query);
359
360     $sth->execute( $shelfnumber, $biblionumber );
361     ($sth->rows) and return; # already on shelf
362     $query = qq(
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 );
373 }
374
375 =head2 ModShelf
376
377 my $result= ModShelf($shelfnumber, $hashref)
378
379 Where $hashref->{column} = param
380
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.
385
386 Returns 1 if the action seemed to be successful.
387
388 =cut
389
390 sub ModShelf {
391     my ($shelfnumber,$hashref) = @_;
392     my $dbh = C4::Context->dbh;
393
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?
399
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->{$_};
403     }
404
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},
410             $oldrecord->{owner},
411             $shelfnumber )) {
412                 return 0; #name check failed
413         }
414     }
415
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);
419     $sth->execute(
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},
426         $shelfnumber );
427     return $@? 0: 1;
428 }
429
430 =head2 ShelfPossibleAction
431
432 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
433
434 C<$loggedinuser,$shelfnumber,$action>
435
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.
441
442 Returns 1 if the user can do the $action in the $shelfnumber shelf.
443 Returns 0 otherwise.
444
445 =cut
446
447 sub ShelfPossibleAction {
448     my ( $user, $shelfnumber, $action ) = @_;
449     $action= 'view' unless $action;
450     $user=0 unless $user;
451
452     if($action =~ /^new/) { #no shelfnumber needed
453         if($action eq 'new_private') {
454             return $user>0;
455         }
456         elsif($action eq 'new_public') {
457             return $user>0 && C4::Context->preference('OpacAllowPublicListCreation');
458         }
459         return 0;
460     }
461
462     return 0 unless defined($shelfnumber);
463
464     my $dbh = C4::Context->dbh;
465     my $query = qq/
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=?
471     /;
472     my $sth = $dbh->prepare($query);
473     $sth->execute($user, $shelfnumber);
474     my $shelf= $sth->fetchrow_hashref;
475
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
479         return 1;
480     }
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
485     }
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);
492     }
493     elsif($action eq 'manage') {
494         return 1 if $user && $shelf->{owner}==$user;
495     }
496     return 0;
497 }
498
499 =head2 DelFromShelf
500
501     $result= &DelFromShelf( $bibref, $shelfnumber, $user);
502
503 Removes biblionumbers in passed arrayref from shelf C<$shelfnumber>.
504 If the bib wasn't on that virtualshelves to begin with, nothing happens.
505
506 Returns 0 if no items have been deleted.
507
508 =cut
509
510 sub DelFromShelf {
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;
517     my $r; my $t=0;
518
519     if($del_own) {
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;
527         }
528     }
529     if($del_oth) {
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);
537             $r= $sth->rows;
538             $t+= ($r==-1)? 1: $r;
539         }
540     }
541     return $t;
542 }
543
544 =head2 DelShelf
545
546   $Number = DelShelf($shelfnumber);
547
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.
551
552 =cut
553
554 sub DelShelf {
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);
560 }
561
562 =head2 GetBibliosShelves
563
564 This finds all the public lists that this bib record is in.
565
566 =cut
567
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) 
575         WHERE vs.category=2
576         AND vc.biblionumber= ?
577     ');
578     $sth->execute( $biblionumber );
579     return $sth->fetchall_arrayref({});
580 }
581
582 =head2 ShelvesMax
583
584     $howmany= ShelvesMax($context);
585
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.
590
591 =cut
592
593 sub ShelvesMax {
594     my $which= shift;
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;
600 }
601
602 =head2 HandleDelBorrower
603
604      HandleDelBorrower($borrower);
605
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.
610
611 =cut
612
613 sub HandleDelBorrower {
614     my ($borrower)= @_;
615     my $query;
616     my $dbh = C4::Context->dbh;
617
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));
623
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";
626     $dbh->do($query);
627
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));
631
632     #Delete unshared private lists
633     $query="DELETE FROM virtualshelves WHERE owner=? AND category=1";
634     $dbh->do($query,undef,($borrower));
635
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));
639
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));
643 }
644
645 =head2 AddShare
646
647      AddShare($shelfnumber, $key);
648
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
653 expiry date.
654
655 =cut
656
657 sub AddShare {
658     my ($shelfnumber, $key)= @_;
659     return if !$shelfnumber || !$key;
660
661     my $sql;
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
665     $dbh->do($sql);
666     $sql="INSERT INTO virtualshelfshares (shelfnumber, invitekey, sharedate) VALUES (?, ?, ADDDATE(NOW(),?))";
667     $dbh->do($sql, undef, ($shelfnumber, $key, SHARE_INVITATION_EXPIRY_DAYS));
668 }
669
670 # internal subs
671
672 sub _shelf_count {
673     my ($owner, $category) = @_;
674     my @params;
675     # Find out how many shelves total meet the submitted criteria...
676
677     my $dbh = C4::Context->dbh;
678     my $query = "SELECT count(*) FROM virtualshelves vs ";
679     if($category==1) {
680         $query.= qq{
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);
685     }
686     else {
687         $query.='WHERE category=2';
688         @params= ();
689     }
690     my $sth = $dbh->prepare($query);
691     $sth->execute(@params);
692     my ($total)= $sth->fetchrow;
693     return $total;
694 }
695
696 sub _CheckShelfName {
697     my ($name, $cat, $owner, $number)= @_;
698
699     my $dbh = C4::Context->dbh;
700     my @pars;
701     my $query = qq(
702         SELECT DISTINCT shelfnumber
703         FROM   virtualshelves
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);
709     }
710     elsif($cat==1 && !defined($owner)) { #owner is null (exceptional)
711         $query.= ' AND owner IS NULL AND category=1';
712         @pars=($name, $number);
713     }
714     else { #public list
715         $query.= ' AND category=2';
716         @pars=($name, $number);
717     }
718     my $sth = $dbh->prepare($query);
719     $sth->execute(@pars);
720     return $sth->rows>0? 0: 1;
721 }
722
723 1;
724
725 __END__
726
727 =head1 AUTHOR
728
729 Koha Development Team <http://koha-community.org/>
730
731 =head1 SEE ALSO
732
733 C4::Circulation::Circ2(3)
734
735 =cut