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