3 # Copyright 2013 Rijksmuseum
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use constant KEYLENGTH
=> 10;
23 use constant TEMPLATE_NAME
=> 'opac-shareshelf.tt';
24 use constant SHELVES_URL
=>
25 '/cgi-bin/koha/opac-shelves.pl?display=privateshelves&viewshelf=';
37 use Koha
::Virtualshelves
;
38 use Koha
::Virtualshelfshares
;
41 # if virtualshelves is disabled, leave immediately
42 if ( ! C4
::Context
->preference('virtualshelves') ) {
44 print $query->redirect("/cgi-bin/koha/errors/404.pl");
48 #-------------------------------------------------------------------------------
50 my $pvar = _init
( {} );
51 if ( !$pvar->{errcode
} ) {
52 show_invite
($pvar) if $pvar->{op
} eq 'invite';
53 confirm_invite
($pvar) if $pvar->{op
} eq 'conf_invite';
54 show_accept
($pvar) if $pvar->{op
} eq 'accept';
56 load_template_vars
($pvar);
57 output_html_with_http_headers
$pvar->{query
}, $pvar->{cookie
},
58 $pvar->{template
}->output;
60 #-------------------------------------------------------------------------------
65 $param->{query
} = $query;
66 $param->{shelfnumber
} = $query->param('shelfnumber') || 0;
67 $param->{op
} = $query->param('op') || '';
68 $param->{addrlist
} = $query->param('invite_address') || '';
69 $param->{key
} = $query->param('key') || '';
70 $param->{appr_addr
} = [];
71 $param->{fail_addr
} = [];
72 $param->{errcode
} = check_common_errors
($param);
75 if ( $param->{addrlist
} ) {
76 $param->{addrlist
} =~ s
|^\s
+||;
77 $param->{addrlist
} =~ s
|\s
+$||;
80 #get some list details
82 my $shelfnumber = $param->{shelfnumber
};
83 $shelf = Koha
::Virtualshelves
->find( $shelfnumber ) unless $param->{errcode
};
84 $param->{shelfname
} = $shelf ?
$shelf->shelfname : q
||;
85 $param->{owner
} = $shelf ?
$shelf->owner : -1;
86 $param->{category
} = $shelf ?
$shelf->category : -1;
88 load_template
($param);
92 sub check_common_errors
{
94 if ( $param->{op
} !~ /^(invite|conf_invite|accept)$/ ) {
95 return 1; #no operation specified
97 if ( $param->{shelfnumber
} !~ /^\d+$/ ) {
98 return 2; #invalid shelf number
100 if ( !C4
::Context
->preference('OpacAllowSharingPrivateLists') ) {
101 return 3; #not or no longer allowed?
108 return unless check_owner_category
($param);
113 return unless check_owner_category
($param);
114 process_addrlist
($param);
115 if ( @
{ $param->{appr_addr
} } ) {
116 send_invitekey
($param);
119 $param->{errcode
} = 6; #not one valid address
126 my $shelfnumber = $param->{shelfnumber
};
127 my $shelf = Koha
::Virtualshelves
->find( $shelfnumber );
129 # The key for accepting is checked later in Koha::Virtualshelfshare
130 # You must not be the owner and the list must be private
132 $param->{errcode
} = 2;
133 } elsif( $shelf->category == 2 ) {
134 $param->{errcode
} = 5;
135 } elsif( $shelf->owner == $param->{loggedinuser
} ) {
136 $param->{errcode
} = 8;
138 return if $param->{errcode
};
140 # Look for shelfnumber and invitekey in shares, expiration check later
141 my $key = keytostring
( stringtokey
( $param->{key
}, 0 ), 1 );
142 my $shared_shelves = Koha
::Virtualshelfshares
->search({
143 shelfnumber
=> $param->{shelfnumber
},
146 my $shared_shelf = $shared_shelves ?
$shared_shelves->next : undef; # we pick the first, but there should only be one
148 if ( $shared_shelf ) {
149 my $is_accepted = eval { $shared_shelf->accept( $key, $param->{loggedinuser
} ) };
151 notify_owner
($param);
152 #redirect to view of this shared list
153 print $param->{query
}->redirect(
154 -uri
=> SHELVES_URL
. $param->{shelfnumber
},
155 -cookie
=> $param->{cookie
}
160 $param->{errcode
} = 7; # not accepted: key invalid or expired
166 my $patron = Koha
::Patrons
->find( $param->{owner
} );
167 return unless $patron;
169 my $toaddr = $patron->notice_email_address or return;
172 my $letter = C4
::Letters
::GetPreparedLetter
(
174 letter_code
=> 'SHARE_ACCEPT',
175 branchcode
=> C4
::Context
->userenv->{"branch"},
176 lang
=> $patron->lang,
177 tables
=> { borrowers
=> $param->{loggedinuser
}, },
178 substitute
=> { listname
=> $param->{shelfname
}, },
181 #send letter to queue
182 C4
::Letters
::EnqueueLetter
(
185 message_transport_type
=> 'email',
186 from_address
=> C4
::Context
->preference('KohaAdminEmailAddress'),
187 to_address
=> $toaddr,
192 sub process_addrlist
{
194 my @temp = split /[,:;]/, $param->{addrlist
};
197 foreach my $a (@temp) {
200 if ( IsEmailAddress
($a) ) {
207 $param->{appr_addr
} = \
@appr_addr;
208 $param->{fail_addr
} = \
@fail_addr;
213 my $fromaddr = C4
::Context
->preference('KohaAdminEmailAddress');
215 C4
::Context
->preference('OPACBaseURL')
216 . "/cgi-bin/koha/opac-shareshelf.pl?shelfnumber="
217 . $param->{shelfnumber
}
220 #TODO Waiting for the right http or https solution (BZ 8952 a.o.)
222 my @ok; #the addresses that were processed well
223 foreach my $a ( @
{ $param->{appr_addr
} } ) {
224 my @newkey = randomlist
( KEYLENGTH
, 64 ); #generate a new key
226 #add a preliminary share record
227 my $shelf = Koha
::Virtualshelves
->find( $param->{shelfnumber
} );
228 my $key = keytostring
( \
@newkey, 1 );
229 my $is_shared = eval { $shelf->share( $key ); };
230 # TODO Better error handling, catch the exceptions
231 if ( $@
or not $is_shared ) {
232 push @
{ $param->{fail_addr
} }, $a;
238 my $letter = C4
::Letters
::GetPreparedLetter
(
240 letter_code
=> 'SHARE_INVITE',
241 branchcode
=> C4
::Context
->userenv->{"branch"},
242 lang
=> 'default', # Not sure how we could use something more useful else here
243 tables
=> { borrowers
=> $param->{loggedinuser
}, },
245 listname
=> $param->{shelfname
},
246 shareurl
=> $url . keytostring
( \
@newkey, 0 ),
250 #send letter to queue
251 C4
::Letters
::EnqueueLetter
(
254 message_transport_type
=> 'email',
255 from_address
=> $fromaddr,
260 $param->{appr_addr
} = \
@ok;
263 sub check_owner_category
{
266 #sharing user should be the owner
267 #list should be private
268 $param->{errcode
} = 4 if $param->{owner
} != $param->{loggedinuser
};
269 $param->{errcode
} = 5 if !$param->{errcode
} && $param->{category
} != 1;
270 return !defined $param->{errcode
};
275 ( $param->{template
}, $param->{loggedinuser
}, $param->{cookie
} ) =
276 get_template_and_user
(
278 template_name
=> TEMPLATE_NAME
,
279 query
=> $param->{query
},
281 authnotrequired
=> 0, #should be a user
286 sub load_template_vars
{
288 my $template = $param->{template
};
289 my $appr = join '; ', @
{ $param->{appr_addr
} };
290 my $fail = join '; ', @
{ $param->{fail_addr
} };
292 errcode
=> $param->{errcode
},
294 shelfnumber
=> $param->{shelfnumber
},
295 shelfname
=> $param->{shelfname
},
296 approvedaddress
=> $appr,
297 failaddress
=> $fail,
303 #TODO candidate for a module?
304 return Email
::Valid
->address( $_[0] ) ?
1 : 0;
309 #uses rand, safe enough for this application but not for more sensitive data
310 my ( $length, $base ) = @_;
311 return map { int( rand($base) ); } 1 .. $length;
315 my ( $keyref, $flgBase64 ) = @_;
317 my $alphabet = [ 'A' .. 'Z', 'a' .. 'z', 0 .. 9, '+', '/' ];
318 return join '', map { alphabet_char
( $_, $alphabet ); } @
$keyref;
320 return join '', map { sprintf( "%02d", $_ ); } @
$keyref;
324 my ( $str, $flgBase64 ) = @_;
325 my @temp = split '', $str || '';
327 my $alphabet = [ 'A' .. 'Z', 'a' .. 'z', 0 .. 9, '+', '/' ];
328 return [ map { alphabet_ordinal
( $_, $alphabet ); } @temp ];
330 return [] if $str !~ /^\d+$/;
332 for ( my $i = 0 ; $i < @temp - 1 ; $i += 2 ) {
333 push @retval, $temp[$i] * 10 + $temp[ $i + 1 ];
338 sub alphabet_ordinal
{
339 my ( $char, $alphabet ) = @_;
340 for my $ord ( 0 .. $#$alphabet ) {
341 return $ord if $char eq $alphabet->[$ord];
343 return ''; #ignore missing chars
348 #reverse operation for ordinal; ignore invalid numbers
349 my ( $num, $alphabet ) = @_;
350 return $num =~ /^\d+$/ && $num <= $#$alphabet ? $alphabet->[$num] : '';