Update release notes for 23.05.12 release
[koha.git] / Koha / Virtualshelf.pm
1 package Koha::Virtualshelf;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20
21 use C4::Auth;
22
23 use Koha::Patrons;
24 use Koha::Database;
25 use Koha::DateUtils qw( dt_from_string );
26 use Koha::Exceptions;
27 use Koha::Exceptions::Virtualshelf;
28 use Koha::Virtualshelfshare;
29 use Koha::Virtualshelfshares;
30 use Koha::Virtualshelfcontent;
31 use Koha::Virtualshelfcontents;
32
33 use base qw(Koha::Object);
34
35 =head1 NAME
36
37 Koha::Virtualshelf - Koha Virtualshelf Object class
38
39 =head1 API
40
41 =head2 Class methods
42
43 =cut
44
45 sub store {
46     my ( $self ) = @_;
47
48     unless ( $self->owner ) {
49         Koha::Exceptions::Virtualshelf::UseDbAdminAccount->throw;
50     }
51
52     unless ( $self->is_shelfname_valid ) {
53         Koha::Exceptions::Virtualshelf::DuplicateObject->throw;
54     }
55
56     $self->allow_change_from_owner( 1 )
57         unless defined $self->allow_change_from_owner;
58     $self->allow_change_from_others( 0 )
59         unless defined $self->allow_change_from_others;
60     $self->allow_change_from_staff( 0 )
61         unless defined $self->allow_change_from_staff;
62     $self->allow_change_from_permitted_staff( 0 )
63         unless defined $self->allow_change_from_permitted_staff;
64
65     $self->created_on( dt_from_string )
66         unless defined $self->created_on;
67
68     return $self->SUPER::store( $self );
69 }
70
71 sub is_public {
72     my ( $self ) = @_;
73     return $self->public;
74 }
75
76 sub is_private {
77     my ( $self ) = @_;
78     return !$self->public;
79 }
80
81 sub is_shelfname_valid {
82     my ( $self ) = @_;
83
84     my $conditions = {
85         shelfname => $self->shelfname,
86         ( $self->shelfnumber ? ( "me.shelfnumber" => { '!=', $self->shelfnumber } ) : () ),
87     };
88
89     if ( $self->is_private and defined $self->owner ) {
90         $conditions->{-or} = {
91             "virtualshelfshares.borrowernumber" => $self->owner,
92             "me.owner" => $self->owner,
93         };
94         $conditions->{public} = 0;
95     }
96     elsif ( $self->is_private and not defined $self->owner ) {
97         $conditions->{owner} = undef;
98         $conditions->{public} = 0;
99     }
100     else {
101         $conditions->{public} = 1;
102     }
103
104     my $count = Koha::Virtualshelves->search(
105         $conditions,
106         {
107             join => 'virtualshelfshares',
108         }
109     )->count;
110     return $count ? 0 : 1;
111 }
112
113 sub get_shares {
114     my ( $self ) = @_;
115     my $rs = $self->_result->virtualshelfshares;
116     my $shares = Koha::Virtualshelfshares->_new_from_dbic( $rs );
117     return $shares;
118 }
119
120 sub get_contents {
121     my ( $self ) = @_;
122     my $rs = $self->_result->virtualshelfcontents;
123     my $contents = Koha::Virtualshelfcontents->_new_from_dbic( $rs );
124     return $contents;
125 }
126
127 sub share {
128     my ( $self, $key ) = @_;
129     unless ( $key ) {
130         Koha::Exceptions::Virtualshelf::InvalidKeyOnSharing->throw;
131     }
132     Koha::Virtualshelfshare->new(
133         {
134             shelfnumber => $self->shelfnumber,
135             invitekey => $key,
136             sharedate => dt_from_string,
137         }
138     )->store;
139 }
140
141 sub is_shared {
142     my ( $self ) = @_;
143     return  $self->get_shares->search(
144         {
145             borrowernumber => { '!=' => undef },
146         }
147     )->count;
148 }
149
150 sub is_shared_with {
151     my ( $self, $borrowernumber ) = @_;
152     return unless $borrowernumber;
153     return  $self->get_shares->search(
154         {
155             borrowernumber => $borrowernumber,
156         }
157     )->count;
158 }
159
160 sub remove_share {
161     my ( $self, $borrowernumber ) = @_;
162     my $shelves = Koha::Virtualshelfshares->search(
163         {
164             shelfnumber => $self->shelfnumber,
165             borrowernumber => $borrowernumber,
166         }
167     );
168     return 0 unless $shelves->count;
169
170     # Only 1 share with 1 patron can exist
171     return $shelves->next->delete;
172 }
173
174 sub add_biblio {
175     my ( $self, $biblionumber, $borrowernumber ) = @_;
176     return unless $biblionumber;
177     my $already_exists = $self->get_contents->search(
178         {
179             biblionumber => $biblionumber,
180         }
181     )->count;
182     return if $already_exists;
183
184     # Check permissions
185     my $patron = Koha::Patrons->find( $borrowernumber ) or return 0;
186     return 0 unless ( $self->owner == $borrowernumber && $self->allow_change_from_owner ) || ( $self->allow_change_from_staff && $patron->can_patron_change_staff_only_lists ) || ( $self->allow_change_from_permitted_staff && $patron->can_patron_change_permitted_staff_lists ) || $self->allow_change_from_others;
187
188     my $content = Koha::Virtualshelfcontent->new(
189         {
190             shelfnumber => $self->shelfnumber,
191             biblionumber => $biblionumber,
192             borrowernumber => $borrowernumber,
193         }
194     )->store;
195     $self->lastmodified(dt_from_string);
196     $self->store;
197
198     return $content;
199 }
200
201 sub remove_biblios {
202     my ( $self, $params ) = @_;
203     my $biblionumbers = $params->{biblionumbers} || [];
204     my $borrowernumber = $params->{borrowernumber};
205     return unless @$biblionumbers;
206
207     my $number_removed = 0;
208     my $patron = Koha::Patrons->find( $borrowernumber ) or return 0;
209     if( ( $self->owner == $borrowernumber && $self->allow_change_from_owner )
210       || ( $self->allow_change_from_staff && $patron->can_patron_change_staff_only_lists )
211       || ( $self->allow_change_from_permitted_staff && $patron->can_patron_change_permitted_staff_lists )
212       || $self->allow_change_from_others ) {
213         $number_removed += $self->get_contents->search({
214             biblionumber => $biblionumbers,
215         })->delete;
216     }
217     return $number_removed;
218 }
219
220 sub can_be_viewed {
221     my ( $self, $borrowernumber ) = @_;
222     return 1 if $self->is_public;
223     return 0 unless $borrowernumber;
224     return 1 if $self->owner == $borrowernumber;
225     return $self->get_shares->search(
226         {
227             borrowernumber => $borrowernumber,
228         }
229     )->count;
230 }
231
232 sub can_be_deleted {
233     my ( $self, $borrowernumber ) = @_;
234
235     return 0 unless $borrowernumber;
236     return 1 if $self->owner == $borrowernumber;
237
238     my $patron = Koha::Patrons->find( $borrowernumber ) or return 0;
239
240     return 1 if $self->is_public and C4::Auth::haspermission( $patron->userid, { lists => 'delete_public_lists' } );
241
242     return 0;
243 }
244
245 sub can_be_managed {
246     my ( $self, $borrowernumber ) = @_;
247     return 1
248       if $borrowernumber and $self->owner == $borrowernumber;
249
250     my $patron = Koha::Patrons->find( $borrowernumber ) or return 0;
251     return 1
252       if $self->is_public and C4::Auth::haspermission( $patron->userid, { lists => 'edit_public_lists' } );
253     return 0;
254 }
255
256 sub can_biblios_be_added {
257     my ( $self, $borrowernumber ) = @_;
258
259     my $patron = Koha::Patrons->find( $borrowernumber ) or return 0;
260     return 1
261       if $borrowernumber
262       and ( ( $self->owner == $borrowernumber && $self->allow_change_from_owner ) or ( $self->allow_change_from_staff && $patron->can_patron_change_staff_only_lists ) or ( $self->allow_change_from_permitted_staff && $patron->can_patron_change_permitted_staff_lists ) or $self->allow_change_from_others );
263     return 0;
264 }
265
266 sub can_biblios_be_removed {
267     my ( $self, $borrowernumber ) = @_;
268     return $self->can_biblios_be_added( $borrowernumber );
269     # Same answer since bug 18228
270 }
271
272 =head3 cannot_be_transferred
273
274     $shelf->cannot_be_transferred({
275         by => $p1, to => $p2, interface => opac|intranet|undef,
276             # p1 and p2 are borrowernumbers
277     });
278
279     This routine has two main goals:
280     [1] Decide if patron may transfer a shared list to another
281         sharee (patron).
282     [2] Decide if staff member may transfer a public list.
283
284     If you pass interface, we'll check if it supports transfer too.
285     NOTE: The explicit passing is still more reliable than via context,
286     since we could switch interface after login in the same session.
287
288     Returns a true value (read: error_code) when not allowed.
289     The following error codes are possible:
290         unauthorized_transfer, missing_by_parameter, new_owner_not_found,
291         new_owner_has_no_share, missing_to_parameter.
292     Otherwise returns false (zero).
293
294 =cut
295
296 sub cannot_be_transferred {
297     my ( $self, $params ) = @_;
298     my $to = $params->{to};
299     my $by = $params->{by};
300     my $interface = $params->{interface};
301
302     # Check on interface: currently we don't support transfer shared on intranet, transfer public on OPAC
303     if( $interface ) {
304         return 'unauthorized_transfer'
305             if ( $self->public && $interface eq 'opac' ) or
306                ( $self->is_private && $interface eq 'intranet' );
307                    # is_private call is enough here, get_shares tested below
308     }
309
310     my $shares = $self->public ? undef : $self->get_shares->search({ borrowernumber => { '!=' => undef } });
311     return 'unauthorized_transfer' if $self->is_private && !$shares->count;
312
313     if( $by ) {
314         if( $self->public ) {
315             my $by_patron = Koha::Patrons->find($by);
316             return 'unauthorized_transfer'
317                 if !$by_patron || !C4::Auth::haspermission( $by_patron->userid, { lists => 'edit_public_lists' });
318         } else {
319             return 'unauthorized_transfer' if !$self->can_be_managed($by);
320         }
321     } else {
322         return 'missing_by_parameter';
323     }
324
325     if( $to ) {
326         if( !Koha::Patrons->find($to) ) {
327             return 'new_owner_not_found';
328         }
329         if( !$self->public && !$shares->search({ borrowernumber => $to })->count ) {
330             return 'new_owner_has_no_share';
331         }
332     } else {
333         return 'missing_to_parameter';
334     }
335     return 0; # serving as green light
336 }
337
338 =head3 transfer_ownership
339
340     $list->transfer_ownership( $patron_id );
341
342 This method transfers the list ownership to the passed I<$patron_id>.
343
344 =cut
345
346 sub transfer_ownership {
347     my ( $self, $patron_id ) = @_;
348
349     Koha::Exceptions::MissingParameter->throw( "Mandatory parameter 'patron' missing" )
350       unless $patron_id;
351
352     $self->remove_share( $patron_id ) if $self->is_private;
353     return $self->set({ owner => $patron_id })->store;
354 }
355
356 =head2 Internal methods
357
358 =head3 _type
359
360 =cut
361
362 sub _type {
363     return 'Virtualshelve';
364 }
365
366 1;