Bug 13417: (qa follow-up) Rename the permission to delete_public_lists
[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::Auth;
25 use C4::Context;
26 use C4::Debug;
27 use C4::Members;
28
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
33
34 use constant SHARE_INVITATION_EXPIRY_DAYS => 14; #two weeks to accept
35
36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
37
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
51     );
52         @EXPORT_OK = qw(
53             &GetAllShelves &ShelvesMax
54         );
55 }
56
57
58 =head1 NAME
59
60 C4::VirtualShelves - Functions for manipulating Koha virtual shelves
61
62 =head1 SYNOPSIS
63
64   use C4::VirtualShelves;
65
66 =head1 DESCRIPTION
67
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.
71
72 =head1 FUNCTIONS
73
74 =head2 GetShelves
75
76   $shelflist = &GetShelves($category, $row_count, $offset, $owner);
77   ($shelfnumber, $shelfhash) = each %{$shelflist};
78
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>.
83
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.
85
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:
88
89 =over
90
91 =item C<$shelfhash-E<gt>{shelfname}>
92
93 A string. The name of the shelf.
94
95 =back
96
97 =cut
98
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;
117     }
118     else {
119         $query.= 'WHERE category=2 ';
120     }
121     $query.= qq{
122         GROUP BY vs.shelfnumber
123         ORDER BY vs.shelfname
124         LIMIT ?, ?};
125
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;
138     }
139     return \%shelflist;
140 }
141
142 =head2 GetAllShelves
143
144     $shelflist = GetAllShelves($category, $owner)
145
146 This function returns a reference to an array of hashrefs containing all shelves
147 sorted by the shelf name.
148
149 This function is intended to return a dataset reflecting all the shelves for
150 the submitted parameters.
151
152 =cut
153
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);
165     }
166     else {
167     $query.='WHERE category=2 ';
168         @params = ();
169     }
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({});
176 }
177
178 =head2 GetSomeShelfNames
179
180 Returns shelf names and numbers for Add to combo of search results and Lists button of OPAC header.
181
182 =cut
183
184 sub GetSomeShelfNames {
185     my ($owner, $purpose, $adding_allowed)= @_;
186     my ($bar, $pub, @params);
187     my $dbh = C4::Context->dbh;
188
189     my $bquery = 'SELECT vs.shelfnumber, vs.shelfname FROM virtualshelves vs ';
190     my $limit= ShelvesMax($purpose);
191
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";
196
197     unless($adding_allowed && (!defined($owner) || $owner<=0)) {
198         #if adding items, user should be known
199         $pub= $dbh->selectall_arrayref($qry1,{Slice=>{}},@params);
200     }
201
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);
212     }
213
214     return ( { bartotal => $bar? scalar @$bar: 0, pubtotal => $pub? scalar @$pub: 0}, $pub, $bar);
215 }
216
217 =head2 GetShelf
218
219   (shelfnumber,shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other) = &GetShelf($shelfnumber);
220
221 Returns the above-mentioned fields for passed virtual shelf number.
222
223 =cut
224
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=?
233     );
234     my $sth = $dbh->prepare($query);
235     $sth->execute($shelfnumber);
236     return $sth->fetchrow;
237 }
238
239 =head2 GetShelfContents
240
241   $biblist = &GetShelfContents($shelfnumber);
242
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.
246
247 Returns a reference-to-array, whose elements are references-to-hash,
248 as returned by C<C4::Biblio::GetBiblioFromItemNumber>.
249
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.
254
255 =cut
256
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;
267     }
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' );
281     }
282     if($row_count){
283        $query .= " LIMIT ?, ? ";
284        push (@params, ($offset ? $offset : 0));
285        push (@params, $row_count);
286     }
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.
296 }
297
298 =head2 AddShelf
299
300   $shelfnumber = &AddShelf($hashref, $owner);
301
302 Creates a new virtual shelf. Params passed in a hash like ModShelf.
303
304 Returns a code to know what's happen.
305     * -1 : if this virtualshelves already exists.
306     * $shelfnumber : if success.
307
308 =cut
309
310 sub AddShelf {
311     my ($hashref, $owner)= @_;
312     my $dbh = C4::Context->dbh;
313
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->{$_};
317     }
318
319     return -1 unless _CheckShelfName($hashref->{shelfname}, $hashref->{category}, $owner, 0);
320
321     my $query = qq(INSERT INTO virtualshelves
322         (shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other)
323         VALUES (?,?,?,?,?,?,?));
324
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;
337 }
338
339 =head2 AddToShelf
340
341   &AddToShelf($biblionumber, $shelfnumber, $borrower);
342
343 Adds bib number C<$biblionumber> to virtual virtualshelves number
344 C<$shelfnumber>, unless that bib is already on that shelf.
345
346 =cut
347
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=?
356     );
357     my $sth = $dbh->prepare($query);
358
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 );
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     my $dbh = C4::Context->dbh;
392
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?
398
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->{$_};
402     }
403
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
412         }
413     }
414
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;
427 }
428
429 =head2 ShelfPossibleAction
430
431 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
432
433 C<$loggedinuser,$shelfnumber,$action>
434
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.
441
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
446
447 =cut
448
449 sub ShelfPossibleAction {
450     my ( $user, $shelfnumber, $action ) = @_;
451     $action= 'view' unless $action;
452     $user=0 unless $user;
453
454     if($action =~ /^new/) { #no shelfnumber needed
455         if($action eq 'new_private') {
456             return $user>0;
457         }
458         elsif($action eq 'new_public') {
459             return $user>0 && C4::Context->preference('OpacAllowPublicListCreation');
460         }
461         return 0;
462     }
463
464     return 0 unless defined($shelfnumber);
465
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_public_lists' } );
470     }
471
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=?
479     /;
480     my $sth = $dbh->prepare($query);
481     $sth->execute($user, $shelfnumber);
482     my $shelf= $sth->fetchrow_hashref;
483
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;
488     }
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
493     }
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);
500     }
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
506         }
507         else {
508             return (0, 5); # code 5: should be private list
509         }
510     }
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;
518         }
519         else {
520             return (0, 5); # code 5: should be private list
521         }
522     }
523     elsif($action eq 'manage' or $action eq 'delete_shelf') {
524         return 1 if $user && $shelf->{owner}==$user;
525     }
526     return 0;
527 }
528
529 =head2 DelFromShelf
530
531     $result= &DelFromShelf( $bibref, $shelfnumber, $user);
532
533 Removes biblionumbers in passed arrayref from shelf C<$shelfnumber>.
534 If the bib wasn't on that virtualshelves to begin with, nothing happens.
535
536 Returns 0 if no items have been deleted.
537
538 =cut
539
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;
548
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;
557         }
558     }
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;
569         }
570     }
571     return $t;
572 }
573
574 =head2 DelShelf
575
576   $Number = DelShelf($shelfnumber);
577
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.
581
582 =cut
583
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);
590 }
591
592 =head2 GetBibliosShelves
593
594 This finds all the public lists that this bib record is in.
595
596 =cut
597
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= ?
607     ');
608     $sth->execute( $biblionumber );
609     return $sth->fetchall_arrayref({});
610 }
611
612 =head2 ShelvesMax
613
614     $howmany= ShelvesMax($context);
615
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.
620
621 =cut
622
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;
630 }
631
632 =head2 HandleDelBorrower
633
634      HandleDelBorrower($borrower);
635
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.
640
641 =cut
642
643 sub HandleDelBorrower {
644     my ($borrower)= @_;
645     my $query;
646     my $dbh = C4::Context->dbh;
647
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));
655
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.
664 }
665
666 =head2 AddShare
667
668      AddShare($shelfnumber, $key);
669
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.
675
676 =cut
677
678 sub AddShare {
679     my ($shelfnumber, $key)= @_;
680     return if !$shelfnumber || !$key;
681
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;
686 }
687
688 =head2 AcceptShare
689
690      my $result= AcceptShare($shelfnumber, $key, $borrowernumber);
691
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.
695
696 =cut
697
698 sub AcceptShare {
699     my ($shelfnumber, $key, $borrowernumber)= @_;
700     return if !$shelfnumber || !$key || !$borrowernumber;
701
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()
708     ";
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;
712 }
713
714 =head2 IsSharedList
715
716      my $bool= IsSharedList( $shelfnumber );
717
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.
723
724 =cut
725
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);
734 }
735
736 =head2 RemoveShare
737
738      RemoveShare( $user, $shelfnumber );
739
740 RemoveShare removes a share for specific shelf and borrower.
741 Returns true if a record could be deleted.
742
743 =cut
744
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=?
751     ";
752     my $n= $dbh->do($sql,undef,($user, $shelfnumber));
753     return if !defined($n) || !$n || $n eq '0E0'; #nothing removed
754     return 1;
755 }
756
757
758 sub GetShelfCount {
759     my ($owner, $category) = @_;
760     my @params;
761     # Find out how many shelves total meet the submitted criteria...
762
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);
771     }
772     else {
773         $query.='WHERE category=2';
774         @params= ();
775     }
776     my $sth = $dbh->prepare($query);
777     $sth->execute(@params);
778     my ($total)= $sth->fetchrow;
779     return $total;
780 }
781
782 # internal subs
783 sub _CheckShelfName {
784     my ($name, $cat, $owner, $number)= @_;
785
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);
796     }
797     elsif($cat==1 && !defined($owner)) { #owner is null (exceptional)
798         $query.= ' AND owner IS NULL AND category=1';
799         @pars=($name, $number);
800     }
801     else { #public list
802         $query.= ' AND category=2';
803         @pars=($name, $number);
804     }
805     my $sth = $dbh->prepare($query);
806     $sth->execute(@pars);
807     return $sth->rows>0? 0: 1;
808 }
809
810 1;
811
812 __END__
813
814 =head1 AUTHOR
815
816 Koha Development Team <http://koha-community.org/>
817
818 =head1 SEE ALSO
819
820 C4::Circulation::Circ2(3)
821
822 =cut