Merge branch 'bug2505_patches' of git://git.catalyst.net.nz/koha into to-push
[koha.git] / C4 / UploadedFile.pm
1 package C4::UploadedFile;
2
3 # Copyright (C) 2007 LibLime
4 # Galen Charlton <galen.charlton@liblime.com>
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12 #
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along
18 # with Koha; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 use strict;
22 #use warnings; FIXME - Bug 2505
23 use C4::Context;
24 use C4::Auth qw/get_session/;
25 use IO::File;
26
27 use vars qw($VERSION);
28
29 BEGIN {
30         # set the version for version checking
31         $VERSION = 3.00;
32 }
33
34 =head1 NAME
35
36 C4::UploadedFile - manage files uploaded by the user
37 for later processing.
38
39 =head1 SYNOPSIS
40
41 =over 4
42
43 # create and store data
44 my $uploaded_file = C4::UploadedFile->new($sessionID);
45 my $fileID = $uploaded_file->id();
46 $uploaded_file->name('c:\temp\file.mrc');
47 $uploaded_file->max_size(1024);
48 while ($have_more_data) {
49     $uploaded_file->stash($data, $bytes_read);
50 }
51 $uploaded_file->done();
52
53 # check status of current file upload
54 my $progress = C4::UploadedFile->upload_progress($sessionID);
55
56 # get file handle for reading uploaded file
57 my $uploaded_file = C4::UploadedFile->fetch($fileID);
58 my $fh = $uploaded_file->fh();
59
60 =back
61
62 Stores files uploaded by the user from their web browser.  The
63 uploaded files are temporary and at present are not guaranteed
64 to survive beyond the life of the user's session.
65
66 This module allows for tracking the progress of the file
67 currently being uploaded.
68
69 TODO: implement secure persistant storage of uploaded files.
70
71 =cut
72
73 =head1 METHODS
74
75 =cut
76
77 =head2 new
78
79 =over 4
80
81 my $uploaded_file = C4::UploadedFile->new($sessionID);
82
83 =back
84
85 Creates a new object to represent the uploaded file.  Requires
86 the current session ID.
87
88 =cut
89
90 sub new {
91     my $class = shift;
92     my $sessionID = shift;
93
94     my $self = {};
95
96     $self->{'sessionID'} = $sessionID;
97     $self->{'fileID'} = Digest::MD5::md5_hex(Digest::MD5::md5_hex(time().{}.rand().{}.$$));
98     # FIXME - make staging area configurable
99     my $TEMPROOT = "/tmp";
100     my $OUTPUTDIR = "$TEMPROOT/$sessionID";
101     mkdir $OUTPUTDIR;
102     my $tmp_file_name = "$OUTPUTDIR/$self->{'fileID'}";
103     my $fh = new IO::File $tmp_file_name, "w";
104     unless (defined $fh) {
105         return undef;
106     }
107     $fh->binmode(); # Windows compatibility
108     $self->{'fh'} = $fh;
109     $self->{'tmp_file_name'} = $tmp_file_name;
110     $self->{'max_size'} = 0;
111     $self->{'progress'} = 0;
112     $self->{'name'} = '';
113
114     bless $self, $class;
115     $self->_serialize();
116
117     my $session = get_session($sessionID);
118     $session->param('current_upload', $self->{'fileID'});
119     $session->flush();
120
121     return $self;
122
123 }
124
125 sub _serialize {
126     my $self = shift;
127
128     my $prefix = "upload_" . $self->{'fileID'};
129     my $session = get_session($self->{'sessionID'});
130
131     # temporarily take file handle out of structure
132     my $fh = $self->{'fh'};
133     delete $self->{'fh'};
134     $session->param($prefix, $self);
135     $session->flush();
136     $self->{'fh'} =$fh;
137 }
138
139 =head2 id
140
141 =over 4
142
143 my $fileID = $uploaded_file->id();
144
145 =back
146
147 =cut
148
149 sub id {
150     my $self = shift;
151     return $self->{'fileID'};
152 }
153
154 =head2 name
155
156 =over 4
157
158 my $name = $uploaded_file->name();
159 $uploaded_file->name($name);
160
161 =back
162
163 Accessor method for the name by which the file is to be known.
164
165 =cut
166
167 sub name {
168     my $self = shift;
169     if (@_) {
170         $self->{'name'} = shift;
171         $self->_serialize();
172     } else {
173         return $self->{'name'};
174     }
175 }
176
177 =head2 max_size
178
179 =over 4
180
181 my $max_size = $uploaded_file->max_size();
182 $uploaded_file->max_size($max_size);
183
184 =back
185
186 Accessor method for the maximum size of the uploaded file.
187
188 =cut
189
190 sub max_size {
191     my $self = shift;
192     @_ ? $self->{'max_size'} = shift : $self->{'max_size'};
193 }
194
195 =head2 stash
196
197 =over 4
198
199 $uploaded_file->stash($dataref, $bytes_read);
200
201 =back
202
203 Write C<$dataref> to the temporary file.  C<$bytes_read> represents
204 the number of bytes (out of C<$max_size>) transmitted so far.
205
206 =cut
207
208 sub stash {
209     my $self = shift;
210     my $dataref = shift;
211     my $bytes_read = shift;
212
213     my $fh = $self->{'fh'};
214     print $fh $$dataref;
215
216     my $percentage = int(($bytes_read / $self->{'max_size'}) * 100);
217     if ($percentage > $self->{'progress'}) {
218         $self->{'progress'} = $percentage;
219         $self->_serialize();
220     }
221 }
222
223 =head2 done
224
225 =over 4
226
227 $uploaded_file->done();
228
229 =back
230
231 Indicates that all of the bytes have been uploaded.
232
233 =cut
234
235 sub done {
236     my $self = shift;
237     $self->{'progress'} = 'done';
238     $self->{'fh'}->close();
239     $self->_serialize();
240 }
241
242 =head2 upload_progress
243
244 =over 4
245
246 my $upload_progress = C4::UploadFile->upload_progress($sessionID);
247
248 =back
249
250 Returns (as an integer from 0 to 100) the percentage
251 progress of the current file upload.
252
253 =cut
254
255 sub upload_progress {
256     my ($class, $sessionID) = shift;
257
258     my $session = get_session($sessionID);
259
260     my $fileID = $session->param('current_upload');
261
262     my $reported_progress = 0;
263     if (defined $fileID and $fileID ne "") {
264         my $file = C4::UploadedFile->fetch($sessionID, $fileID);
265         my $progress = $file->{'progress'};
266         if (defined $progress) {
267             if ($progress eq "done") {
268                 $reported_progress = 100;
269             } else {
270                 $reported_progress = $progress;
271             }
272         }
273     }
274     return $reported_progress;
275 }
276
277 =head2 fetch
278
279 =over 4
280
281     my $uploaded_file = C4::UploadedFile->fetch($sessionID, $fileID);
282
283 =back
284
285 Retrieves an uploaded file object from the current session.
286
287 =cut
288
289 sub fetch {
290     my $class = shift;
291     my $sessionID = shift;
292     my $fileID = shift;
293
294     my $session = get_session($sessionID);
295     my $prefix = "upload_$fileID";
296     my $self = $session->param($prefix);
297     my $fh = new IO::File $self->{'tmp_file_name'}, "r";
298     $self->{'fh'} = $fh;
299
300     bless $self, $class;
301     return $self;
302 }
303
304 =head2 fh
305
306 =over
307
308 my $fh = $uploaded_file->fh();
309
310 =back
311
312 Returns an IO::File handle to read the uploaded file.
313
314 =cut
315
316 sub fh {
317     my $self = shift;
318     return $self->{'fh'};
319 }
320
321 1;
322 __END__
323
324 =head1 AUTHOR
325
326 Koha Development Team <info@koha.org>
327
328 Galen Charlton <galen.charlton@liblime.com>
329
330 =cut