View | Details | Raw Unified | Return to bug 14321
Collapse All | Expand All

(-)a/C4/UploadedFile.pm (-309 lines)
Lines 1-309 Link Here
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
(-)a/C4/UploadedFiles.pm (-323 lines)
Lines 1-323 Link Here
1
package C4::UploadedFiles;
2
3
# This file is part of Koha.
4
#
5
# Copyright (C) 2011-2012 BibLibre
6
#
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.
11
#
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.
16
#
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>.
19
20
=head1 NAME
21
22
C4::UploadedFiles - Functions to deal with files uploaded with cataloging plugin upload.pl
23
24
=head1 SYNOPSIS
25
26
    use C4::UploadedFiles;
27
28
    my $filename = $cgi->param('uploaded_file');
29
    my $file = $cgi->upload('uploaded_file');
30
    my $dir = $input->param('dir');
31
32
    # upload file
33
    my $id = C4::UploadedFiles::UploadFile($filename, $dir, $file->handle);
34
35
    # retrieve file infos
36
    my $uploaded_file = C4::UploadedFiles::GetUploadedFile($id);
37
38
    # delete file
39
    C4::UploadedFiles::DelUploadedFile($id);
40
41
=head1 DESCRIPTION
42
43
This module provides basic functions for adding, retrieving and deleting files related to
44
cataloging plugin upload.pl.
45
46
It uses uploaded_files table.
47
48
It is not related to C4::UploadedFile
49
50
=head1 FUNCTIONS
51
52
=cut
53
54
use Modern::Perl;
55
use Digest::SHA;
56
use Fcntl;
57
use Encode;
58
59
use C4::Context;
60
use C4::Koha;
61
62
sub _get_file_path {
63
    my ($hash, $dirname, $filename) = @_;
64
65
    my $upload_path = C4::Context->config('upload_path');
66
    if( !-d "$upload_path/$dirname" ) {
67
        mkdir "$upload_path/$dirname";
68
    }
69
    my $filepath = "$upload_path/$dirname/${hash}_$filename";
70
    $filepath =~ s|/+|/|g;
71
72
    return $filepath;
73
}
74
75
=head2 GetUploadedFile
76
77
    my $file = C4::UploadedFiles::GetUploadedFile($id);
78
79
Returns a hashref containing infos on uploaded files.
80
Hash keys are:
81
82
=over 2
83
84
=item * id: id of the file (same as given in argument)
85
86
=item * filename: name of the file
87
88
=item * dir: directory where file is stored (relative to config variable 'upload_path')
89
90
=back
91
92
It returns undef if file is not found
93
94
=cut
95
96
sub GetUploadedFile {
97
    my ( $hashvalue ) = @_;
98
99
    return unless $hashvalue;
100
101
    my $dbh = C4::Context->dbh;
102
    my $query = qq{
103
        SELECT hashvalue, filename, dir
104
        FROM uploaded_files
105
        WHERE hashvalue = ?
106
    };
107
    my $sth = $dbh->prepare($query);
108
    $sth->execute( $hashvalue );
109
    my $file = $sth->fetchrow_hashref;
110
    if ($file) {
111
        $file->{filepath} = _get_file_path($file->{hashvalue}, $file->{dir},
112
            $file->{filename});
113
    }
114
115
    return $file;
116
}
117
118
=head2 UploadFile
119
120
    my $id = C4::UploadedFiles::UploadFile($filename, $dir, $io_handle);
121
122
Upload a new file and returns its id (its SHA-1 sum, actually).
123
124
Parameters:
125
126
=over 2
127
128
=item * $filename: name of the file
129
130
=item * $dir: directory where to store the file (path relative to config variable 'upload_path'
131
132
=item * $io_handle: valid IO::Handle object, can be retrieved with
133
$cgi->upload('uploaded_file')->handle;
134
135
=back
136
137
=cut
138
139
sub UploadFile {
140
    my ($filename, $dir, $handle) = @_;
141
    $filename = decode_utf8($filename);
142
    if($filename =~ m#(^|/)\.\.(/|$)# or $dir =~ m#(^|/)\.\.(/|$)#) {
143
        warn "Filename or dirname contains '..'. Aborting upload";
144
        return;
145
    }
146
147
    my $buffer;
148
    my $data = '';
149
    while($handle->read($buffer, 1024)) {
150
        $data .= $buffer;
151
    }
152
    $handle->close;
153
154
    my $sha = new Digest::SHA;
155
    $sha->add($data);
156
    $sha->add($filename);
157
    $sha->add($dir);
158
    my $hash = $sha->hexdigest;
159
160
    # Test if this id already exist
161
    my $file = GetUploadedFile($hash);
162
    if ($file) {
163
        return $file->{hashvalue};
164
    }
165
166
    my $file_path = _get_file_path($hash, $dir, $filename);
167
168
    my $out_fh;
169
    # Create the file only if it doesn't exist
170
    unless( sysopen($out_fh, $file_path, O_WRONLY|O_CREAT|O_EXCL) ) {
171
        warn "Failed to open file '$file_path': $!";
172
        return;
173
    }
174
175
    print $out_fh $data;
176
    my $size= tell($out_fh);
177
    close $out_fh;
178
179
    my $dbh = C4::Context->dbh;
180
    my $query = qq{
181
        INSERT INTO uploaded_files (hashvalue, filename, filesize, dir, categorycode, owner) VALUES (?,?,?,?,?,?);
182
    };
183
    my $sth = $dbh->prepare($query);
184
    my $uid= C4::Context->userenv? C4::Context->userenv->{number}: undef;
185
        # uid is null in unit test
186
    if($sth->execute($hash, $filename, $size, $dir, $dir, $uid)) {
187
        return $hash;
188
    }
189
190
    return;
191
}
192
193
=head2 DanglingEntry
194
195
    C4::UploadedFiles::DanglingEntry($id,$isfileuploadurl);
196
197
Determine if a entry is dangling.
198
199
Returns: 2 == no db entry
200
         1 == no plain file
201
         0 == both a file and db entry.
202
        -1 == N/A (undef id / non-file-upload URL)
203
204
=cut
205
206
sub DanglingEntry {
207
    my ($id,$isfileuploadurl) = @_;
208
    my $retval;
209
210
    if (defined($id)) {
211
        my $file = GetUploadedFile($id);
212
        if($file) {
213
            my $file_path = $file->{filepath};
214
            my $file_deleted = 0;
215
            unless( -f $file_path ) {
216
                $retval = 1;
217
            } else {
218
                $retval = 0;
219
            }
220
        }
221
        else {
222
            if ( $isfileuploadurl ) {
223
                $retval = 2;
224
            }
225
            else {
226
                $retval = -1;
227
            }
228
        }
229
    }
230
    else {
231
        $retval = -1;
232
    }
233
    return $retval;
234
}
235
236
=head2 DelUploadedFile
237
238
    C4::UploadedFiles::DelUploadedFile( $hash );
239
240
Remove a previously uploaded file, given its hash value.
241
242
Returns: 1 == file deleted
243
         0 == file not deleted
244
         -1== no file to delete / no meaninful id passed
245
246
=cut
247
248
sub DelUploadedFile {
249
    my ( $hashval ) = @_;
250
    my $retval;
251
252
    if ( $hashval ) {
253
        my $file = GetUploadedFile( $hashval );
254
        if($file) {
255
            my $file_path = $file->{filepath};
256
            my $file_deleted = 0;
257
            unless( -f $file_path ) {
258
                warn "Id $file->{hashvalue} is in database but no plain file found, removing id from database";
259
                $file_deleted = 1;
260
            } else {
261
                if(unlink $file_path) {
262
                    $file_deleted = 1;
263
                }
264
            }
265
266
            unless($file_deleted) {
267
                warn "File $file_path cannot be deleted: $!";
268
            }
269
270
            my $dbh = C4::Context->dbh;
271
            my $query = qq{
272
                DELETE FROM uploaded_files
273
                WHERE hashvalue = ?
274
            };
275
            my $sth = $dbh->prepare($query);
276
            my $numrows = $sth->execute( $hashval );
277
            # if either a DB entry or file was deleted,
278
            # then clearly we have a deletion.
279
            if ($numrows>0 || $file_deleted==1) {
280
                $retval = 1;
281
            }
282
            else {
283
                $retval = 0;
284
            }
285
        }
286
        else {
287
            warn "There was no file for hash $hashval.";
288
            $retval = -1;
289
        }
290
    }
291
    else {
292
        warn "DelUploadFile called without hash value.";
293
        $retval = -1;
294
    }
295
    return $retval;
296
}
297
298
=head2 getCategories
299
300
    getCategories returns a list of upload category codes and names
301
302
=cut
303
304
sub getCategories {
305
    my $cats = C4::Koha::GetAuthorisedValues('UPLOAD');
306
    [ map {{ code => $_->{authorised_value}, name => $_->{lib} }} @$cats ];
307
}
308
309
=head2 httpheaders
310
311
    httpheaders returns http headers for a retrievable upload
312
    Will be extended by report 14282
313
314
=cut
315
316
sub httpheaders {
317
    my $file= shift;
318
    return
319
        ( '-type' => 'application/octet-stream',
320
          '-attachment' => $file, );
321
}
322
323
1;
(-)a/koha-tmpl/intranet-tmpl/lib/jquery/plugins/ajaxfileupload.js (-200 lines)
Lines 1-200 Link Here
1
2
jQuery.extend({
3
4
    createUploadIframe: function(id, uri)
5
	{
6
			//create frame
7
            var frameId = 'jUploadFrame' + id;
8
            
9
            try {
10
                var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
11
                if(typeof uri== 'boolean'){
12
                    io.src = 'javascript:false';
13
                }
14
                else if(typeof uri== 'string'){
15
                    io.src = uri;
16
                }
17
            }
18
            catch(e) {
19
                var io = document.createElement('iframe');
20
                io.id = frameId;
21
                io.name = frameId;
22
            }
23
            io.style.position = 'absolute';
24
            io.style.top = '-1000px';
25
            io.style.left = '-1000px';
26
27
            document.body.appendChild(io);
28
29
            return io			
30
    },
31
    createUploadForm: function(id, fileElementId)
32
	{
33
		//create form	
34
		var formId = 'jUploadForm' + id;
35
		var fileId = 'jUploadFile' + id;
36
		var form = $('<form  action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');	
37
		var oldElement = $('#' + fileElementId);
38
		var newElement = $(oldElement).clone();
39
		$(oldElement).attr('id', fileId);
40
		$(oldElement).before(newElement);
41
		$(oldElement).appendTo(form);
42
		//set attributes
43
		$(form).css('position', 'absolute');
44
		$(form).css('top', '-1200px');
45
		$(form).css('left', '-1200px');
46
		$(form).appendTo('body');		
47
		return form;
48
    },
49
50
    ajaxFileUpload: function(s) {
51
        // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout		
52
        s = jQuery.extend({}, jQuery.ajaxSettings, s);
53
        var id = new Date().getTime()        
54
		var form = jQuery.createUploadForm(id, s.fileElementId);
55
		var io = jQuery.createUploadIframe(id, s.secureuri);
56
		var frameId = 'jUploadFrame' + id;
57
		var formId = 'jUploadForm' + id;		
58
        // Watch for a new set of requests
59
        if ( s.global && ! jQuery.active++ )
60
		{
61
			jQuery.event.trigger( "ajaxStart" );
62
		}            
63
        var requestDone = false;
64
        // Create the request object
65
        var xml = {}   
66
        if ( s.global )
67
            jQuery.event.trigger("ajaxSend", [xml, s]);
68
        // Wait for a response to come back
69
        var uploadCallback = function(isTimeout)
70
		{			
71
			var io = document.getElementById(frameId);
72
            try 
73
			{				
74
				if(io.contentWindow)
75
				{
76
					 xml.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;
77
                	 xml.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
78
					 
79
				}else if(io.contentDocument)
80
				{
81
					 xml.responseText = io.contentDocument.document.body?io.contentDocument.document.body.innerHTML:null;
82
                	xml.responseXML = io.contentDocument.document.XMLDocument?io.contentDocument.document.XMLDocument:io.contentDocument.document;
83
				}						
84
            }catch(e)
85
			{
86
				jQuery.handleError(s, xml, null, e);
87
			}
88
            if ( xml || isTimeout == "timeout") 
89
			{				
90
                requestDone = true;
91
                var status;
92
                try {
93
                    status = isTimeout != "timeout" ? "success" : "error";
94
                    // Make sure that the request was successful or notmodified
95
                    if ( status != "error" )
96
					{
97
                        // process the data (runs the xml through httpData regardless of callback)
98
                        var data = jQuery.uploadHttpData( xml, s.dataType );    
99
                        // If a local callback was specified, fire it and pass it the data
100
                        if ( s.success )
101
                            s.success( data, status );
102
    
103
                        // Fire the global callback
104
                        if( s.global )
105
                            jQuery.event.trigger( "ajaxSuccess", [xml, s] );
106
                    } else
107
                        jQuery.handleError(s, xml, status);
108
                } catch(e) 
109
				{
110
                    status = "error";
111
                    jQuery.handleError(s, xml, status, e);
112
                }
113
114
                // The request was completed
115
                if( s.global )
116
                    jQuery.event.trigger( "ajaxComplete", [xml, s] );
117
118
                // Handle the global AJAX counter
119
                if ( s.global && ! --jQuery.active )
120
                    jQuery.event.trigger( "ajaxStop" );
121
122
                // Process result
123
                if ( s.complete )
124
                    s.complete(xml, status);
125
126
                jQuery(io).unbind()
127
128
                setTimeout(function()
129
									{	try 
130
										{
131
											$(io).remove();
132
											$(form).remove();	
133
											
134
										} catch(e) 
135
										{
136
											jQuery.handleError(s, xml, null, e);
137
										}									
138
139
									}, 100)
140
141
                xml = null
142
143
            }
144
        }
145
        // Timeout checker
146
        if ( s.timeout > 0 ) 
147
		{
148
            setTimeout(function(){
149
                // Check to see if the request is still happening
150
                if( !requestDone ) uploadCallback( "timeout" );
151
            }, s.timeout);
152
        }
153
        try 
154
		{
155
           // var io = $('#' + frameId);
156
			var form = $('#' + formId);
157
			$(form).attr('action', s.url);
158
			$(form).attr('method', 'POST');
159
			$(form).attr('target', frameId);
160
            if(form.encoding)
161
			{
162
                form.encoding = 'multipart/form-data';				
163
            }
164
            else
165
			{				
166
                form.enctype = 'multipart/form-data';
167
            }			
168
            $(form).submit();
169
170
        } catch(e) 
171
		{			
172
            jQuery.handleError(s, xml, null, e);
173
        }
174
        if(window.attachEvent){
175
            document.getElementById(frameId).attachEvent('onload', uploadCallback);
176
        }
177
        else{
178
            document.getElementById(frameId).addEventListener('load', uploadCallback, false);
179
        } 		
180
        return {abort: function () {}};	
181
182
    },
183
184
    uploadHttpData: function( r, type ) {
185
        var data = !type;
186
        data = type == "xml" || data ? r.responseXML : r.responseText;
187
        // If the type is "script", eval it in global context
188
        if ( type == "script" )
189
            jQuery.globalEval( data );
190
        // Get the JavaScript object, if JSON is used.
191
        if ( type == "json" )
192
            eval( "data = " + data );
193
        // evaluate scripts within html
194
        if ( type == "html" )
195
            jQuery("<div>").html(data).evalScripts();
196
			//alert($('param', data).each(function(){alert($(this).attr('value'));}));
197
        return data;
198
    }
199
})
200
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/file-upload.inc (-70 lines)
Lines 1-70 Link Here
1
<!-- AJAX file upload stuff -->
2
<script type="text/javascript" src="[% interface %]/lib/jquery/plugins/ajaxfileupload.js"></script>
3
<script type="text/javascript">
4
    //<![CDATA[
5
    var fileUploadProgressTimer = 0;
6
    var inFileUploadProgressTimer = false;
7
    var fileUploadProgressTimerCanceled = false;
8
    function updateProgress() {
9
        if (inFileUploadProgressTimer) {
10
            // since $.getJSON is asynchronous, wait
11
            // until the last one is finished
12
            return;
13
        }
14
        inFileUploadProgressTimer = true;
15
        $.getJSON("/cgi-bin/koha/tools/upload-file-progress.pl", function(json) {
16
            if (!fileUploadProgressTimerCanceled) {
17
				var bgproperty = (parseInt(json.progress)*2-300)+"px 0px";
18
                $("#fileuploadprogress").css("background-position",bgproperty);
19
				$("#fileuploadpercent").text(json.progress);
20
            }
21
            inFileUploadProgressTimer = false;
22
        });
23
    }
24
    function ajaxFileUpload()
25
    {
26
        fileUploadProgressTimerCanceled = false;
27
		$("#uploadpanel").show();
28
        $("#fileuploadstatus").show();
29
        fileUploadProgressTimer = setInterval("updateProgress()",500);
30
        $.ajaxFileUpload (
31
            {
32
                url:'/cgi-bin/koha/tools/upload-file.pl',
33
                secureuri:false,
34
                global:false,
35
                fileElementId:'fileToUpload',
36
                dataType: 'json',
37
                success: function (data, status) {
38
                    if (data.status == 'denied') {
39
                        $("#fileuploadstatus").hide();
40
                        $("#fileuploadfailed").show();
41
                        $("#fileuploadfailed").text("Upload failed -- no permission to upload files");
42
                    } else if (data.status == 'failed') {
43
                        $("#fileuploadstatus").hide();
44
                        $("#fileuploadfailed").show();
45
                        $("#fileuploadfailed").text("Upload failed -- unable to store file on server");
46
                    } else if (data.status == 'maintenance') {
47
                        $("#fileuploadstatus").hide();
48
                        $("#fileuploadfailed").show();
49
                        $("#fileuploadfailed").text("Upload failed -- database in maintenance state");
50
                    } else {
51
                         $("#uploadedfileid").val(data.fileid);
52
                         $("#fileuploadprogress").css("background-position","0px 0px");
53
						 $("#processfile").show();
54
                        $("#fileuploadpercent").text("100");
55
                    }
56
                    fileUploadProgressTimerCanceled = true;
57
                    clearInterval(fileUploadProgressTimer);
58
                },
59
                error: function (data, status, e) {
60
                    fileUploadProgressTimerCanceled = true;
61
                    alert(e);
62
                    clearInterval(fileUploadProgressTimer);
63
                }
64
            }
65
        )
66
        return false;
67
68
    }
69
    //]]>
70
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload.tt (-103 lines)
Lines 1-103 Link Here
1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml">
4
<head>
5
    <title>Upload plugin</title>
6
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
    <script type="text/javascript" src="[% interface %]/lib/jquery/jquery.js"></script>
8
    <link rel="stylesheet" type="text/css" href="[% themelang %]/css/staff-global.css" />
9
    <script type="text/javascript">
10
        function ValidateForm() {
11
            var filename = document.forms["UploadForm"]["uploaded_file"].value;
12
            if (!filename) {
13
                alert("Please select a file to upload.");
14
                return false;
15
            }
16
            return true;
17
        }
18
    </script>
19
20
</head>
21
<body id="cat_upload" class="cat">
22
23
<div id="doc3" class="yui-t2"><div id="bd"><div id="yui-main">
24
25
[% IF ( success ) %]
26
27
    <script type="text/javascript">
28
        function report() {
29
            var doc   = opener.document;
30
            var field = doc.getElementById("[% index %]");
31
            field.value =  "[% return %]";
32
        }
33
        $(document).ready(function() {
34
            report();
35
        });
36
    </script>
37
38
39
    The file [% uploaded_file | html %] has been successfully uploaded.
40
    <p><input type="button" value="close" onclick="window.close();" /></p>
41
42
[% ELSE %]
43
44
    [% IF ( MissingURL ) %]
45
        <p>Error: The OPAC system preference OPACBaseURL is not configured.</p>
46
        <p><input type="button" value="close" onclick="window.close();" /></p>
47
    [% ELSIF ( error ) %]
48
        <p>Error: Failed to upload file. See logs for details.</p>
49
        <p><input type="button" value="close" onclick="window.close();" /></p>
50
    [% ELSE %]
51
        [% IF (error_upload_path_not_configured) %]
52
          <h2>Configuration error</h2>
53
          <p>Configuration variable 'upload_path' is not configured.</p>
54
          <p>Please configure it in your koha-conf.xml</p>
55
        [% ELSE %]
56
          [% IF (error_nothing_selected) %]
57
              <p class="error">Error: You have to choose the file to upload and select where to upload the file.</p>
58
          [% END %]
59
          [% IF (error_no_file_selected) %]
60
              <p class="error">Error: You have to choose the file to upload.</p>
61
          [% END %]
62
          [% IF (error_no_dir_selected) %]
63
              <p class="error">Error: You have to select where to upload the file.</p>
64
          [% END %]
65
          [% IF (dangling) %]
66
              <p class="error">Error: The URL has no file to retrieve.</p>
67
          [% END %]
68
69
          <h2>Please select the file to upload:</h2>
70
          <form name="UploadForm" method="post" enctype="multipart/form-data" action="/cgi-bin/koha/cataloguing/plugin_launcher.pl" onsubmit="return ValidateForm()">
71
              <input type="hidden" name="from_popup" value="1" />
72
              <input type="hidden" name="plugin_name" value="upload.pl" />
73
              <input type="hidden" name="index" value="[% index %]" />
74
75
              <div>[% filefield %]</div>
76
              <p/>
77
              <div>
78
                  <label for="uploadcategory">Category: </label>
79
                  [% IF uploadcategories %]
80
                      <select id="uploadcategory" name="uploadcategory">
81
                      [% FOREACH cat IN uploadcategories %]
82
                          <option value="[% cat.code %]">[% cat.name %]</option>
83
                      [% END %]
84
                      </select>
85
                  [% ELSE %]
86
                      <input type="hidden" name="uploadcategory" value="CATALOGUING" />
87
                  [% END %]
88
              </div>
89
              <p/>
90
              <fieldset class="action">
91
                  <input type="submit" value="Upload">
92
                  <input type="button" value="Cancel" onclick="window.close();" />
93
              </fieldset>
94
          </form>
95
        [% END %]
96
    [% END %]
97
98
[% END %]
99
100
</div></div></div>
101
102
</body>
103
</html>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload_delete_file.tt (-63 lines)
Lines 1-63 Link Here
1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml">
4
<head>
5
    <title>Upload plugin</title>
6
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
    <script type="text/javascript" src="[% interface %]/lib/jquery/jquery.js"></script>
8
    <link rel="stylesheet" type="text/css" href="[% themelang %]/css/staff-global.css" />
9
    <script type="text/javascript">
10
        //<![CDATA[
11
        function goToUploadPage() {
12
            var url = "/cgi-bin/koha/cataloguing/plugin_launcher.pl?"
13
                + "plugin_name=upload.pl&index=[% index %]";
14
            window.location.href = url;
15
        }
16
        //]]>
17
    </script>
18
19
</head>
20
<body id="cat_upload_delete" class="cat">
21
[% IF ( success ) %]
22
23
    <script type="text/javascript">
24
        function report() {
25
            var doc   = opener.document;
26
            var field = doc.getElementById("[% index %]");
27
            field.value =  "";
28
        }
29
        $(document).ready(function() {
30
            report();
31
        });
32
    </script>
33
34
    <p>The file has been successfully deleted.</p>
35
36
    <input type="button" value="Upload a new file" onclick="goToUploadPage();" />
37
    <input type="button" value="Close" onclick="window.close();" />
38
39
[% ELSE %]
40
41
    [% IF ( MissingURL ) %]
42
        <p>Error: The OPAC system preference OPACBaseURL is not configured.</p>
43
        <p><input type="button" value="close" onclick="window.close();" /></p>
44
    [% ELSIF ( error ) %]
45
        <p>Error: Unable to delete the file.</p>
46
        <p><input type="button" value="close" onclick="window.close();" /></p>
47
    [% ELSE %]
48
        <h2>File deletion</h2>
49
        <p>A file has already been uploaded for this field. Do you want to delete it?</p>
50
        <form method="post" action="/cgi-bin/koha/cataloguing/plugin_launcher.pl">
51
        <input type="hidden" name="plugin_name" value="upload.pl" />
52
        <input type="hidden" name="delete" value="delete" />
53
        <input type="hidden" name="id" value="[% id %]" />
54
        <input type="hidden" name="index" value="[% index %]" />
55
        <input type="button" value="Cancel" onclick="javascript:window.close();" />
56
        <input type="submit" value="Delete" />
57
        </form>
58
    [% END %]
59
60
[% END %]
61
62
</body>
63
</html>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/test/progressbar.tt (-43 lines)
Lines 1-43 Link Here
1
[% INCLUDE 'doc-head-open.inc' %]
2
<title>Koha &rsaquo; Tools &rsaquo; Stage MARC records for import</title>
3
[% INCLUDE 'doc-head-close.inc' %]
4
[% INCLUDE 'file-upload.inc' %]
5
<script type="text/javascript" src="[% themelang %]/js/background-job-progressbar.js"></script>
6
<style type="text/css">
7
	#uploadpanel,#fileuploadstatus,#fileuploadfailed,#jobpanel,#jobstatus,#jobfailed { display : none; }
8
	#fileuploadstatus,#jobstatus { margin:.4em; }
9
    #fileuploadprogress,#jobprogress{ width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; }</style>
10
<script type="text/javascript">
11
//<![CDATA[
12
$(document).ready(function(){
13
});
14
function CheckForm(f) {
15
    submitBackgroundJob(f);
16
    return false;
17
}
18
19
//]]>
20
</script>
21
</head>
22
<body id="test_progressbar" class="test">
23
<div id="doc3" class="yui-t2">
24
   
25
<form method="post" action="progressbarsubmit.pl">
26
<input type="hidden" name="submitted" id="submitted" value="1" />
27
<input type="hidden" name="runinbackground" id="runinbackground" value="" />
28
<input type="hidden" name="completedJobID" id="completedJobID" value="" />
29
30
<input type="button" id="mainformsubmit" onclick="return CheckForm(this.form);" value="Start" />
31
 
32
       <div id="jobpanel">
33
           <div id="jobstatus">Job progress: <div id="jobprogress"></div> <span id="jobprogresspercent">0</span>%</div>
34
           <div id="jobfailed"></div>
35
       </div>
36
  
37
</form>
38
</div>
39
40
<div>
41
Completed: <span id="completed">[% completedJobID %] </span>
42
</div>
43
</body>
(-)a/t/db_dependent/UploadedFile.t (-16 lines)
Lines 1-16 Link Here
1
#!/usr/bin/perl
2
#
3
4
# A simple test for UploadedFile
5
# only ->new is covered
6
7
use strict;
8
use warnings;
9
10
use Test::More tests => 2;
11
12
BEGIN {
13
        use_ok('C4::UploadedFile');
14
}
15
16
ok(my $file = C4::UploadedFile->new());
(-)a/t/db_dependent/UploadedFiles.t (-61 lines)
Lines 1-61 Link Here
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
use File::Temp qw/ tempdir /;
5
use Test::CGI::Multipart;
6
use Test::More tests => 18;
7
use Test::Warn;
8
9
use t::lib::Mocks;
10
11
use C4::Context;
12
use C4::UploadedFiles;
13
14
# This simulates a multipart POST request with a file upload.
15
my $tcm = new Test::CGI::Multipart;
16
$tcm->upload_file(
17
    name => 'testfile',
18
    file => 'testfilename.txt',
19
    value => "This is the content of testfilename.txt",
20
);
21
my $cgi = $tcm->create_cgi;
22
23
my $tempdir = tempdir(CLEANUP => 1);
24
t::lib::Mocks::mock_config('upload_path', $tempdir);
25
26
my $testfilename = $cgi->param('testfile');
27
my $testfile_fh = $cgi->upload('testfile');
28
my $id = C4::UploadedFiles::UploadFile($testfilename, '', $testfile_fh->handle);
29
ok($id, "File uploaded, id is $id");
30
31
my $file = C4::UploadedFiles::GetUploadedFile($id);
32
isa_ok($file, 'HASH', "GetUploadedFiles($id)");
33
foreach my $key (qw(hashvalue filename filepath dir)) {
34
    ok(exists $file->{$key}, "GetUploadedFile($id)->{$key} exists");
35
}
36
37
ok(-e $file->{filepath}, "File $file->{filepath} exists");
38
39
ok(C4::UploadedFiles::DanglingEntry()==-1, "DanglingEntry() returned -1 as expected.");
40
ok(C4::UploadedFiles::DanglingEntry($id)==0, "DanglingEntry($id) returned 0 as expected.");
41
unlink ($file->{filepath});
42
ok(C4::UploadedFiles::DanglingEntry($id)==1, "DanglingEntry($id) returned 1 as expected.");
43
44
open my $fh,">",($file->{filepath});
45
print $fh "";
46
close $fh;
47
48
my $DelResult;
49
is(C4::UploadedFiles::DelUploadedFile($id),1, "DelUploadedFile($id) returned 1 as expected.");
50
warning_like { $DelResult=C4::UploadedFiles::DelUploadedFile($id); } qr/file for hash/, "Expected warning for deleting Dangling Entry.";
51
is($DelResult,-1, "DelUploadedFile($id) returned -1 as expected.");
52
ok(! -e $file->{filepath}, "File $file->{filepath} does not exist anymore");
53
54
my $UploadResult;
55
warning_like { $UploadResult=C4::UploadedFiles::UploadFile($testfilename,'../',$testfile_fh->handle); } qr/^Filename or dirname contains '..'. Aborting upload/, "Expected warning for bad file upload.";
56
is($UploadResult, undef, "UploadFile with dir containing \"..\" return undef");
57
is(C4::UploadedFiles::GetUploadedFile(), undef, 'GetUploadedFile without parameters returns undef');
58
59
#trivial test for httpheaders
60
my @hdrs = C4::UploadedFiles::httpheaders('does_not_matter_yet');
61
is( @hdrs == 4 && $hdrs[1] =~ /application\/octet-stream/, 1, 'Simple test for httpheaders'); #TODO Will be extended on report 14282
(-)a/test/progressbar.pl (-55 lines)
Lines 1-55 Link Here
1
#!/usr/bin/perl
2
3
# Script for testing progressbar, part 1 - initial screem
4
# it is split into two scripts so we can use firebug to debug it
5
6
# Koha library project  www.koha-community.org
7
8
# Licensed under the GPL
9
10
# Copyright 2010 Catalyst IT, Ltd
11
#
12
# This file is part of Koha.
13
#
14
# Koha is free software; you can redistribute it and/or modify it
15
# under the terms of the GNU General Public License as published by
16
# the Free Software Foundation; either version 3 of the License, or
17
# (at your option) any later version.
18
#
19
# Koha is distributed in the hope that it will be useful, but
20
# WITHOUT ANY WARRANTY; without even the implied warranty of
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
# GNU General Public License for more details.
23
#
24
# You should have received a copy of the GNU General Public License
25
# along with Koha; if not, see <http://www.gnu.org/licenses>.
26
27
use strict;
28
use warnings;
29
30
# standard or CPAN modules used
31
use CGI qw ( -utf8 );
32
use CGI::Cookie;
33
34
# Koha modules used
35
use C4::Context;
36
use C4::Auth;
37
use C4::Output;
38
use C4::BackgroundJob;
39
40
my $input = new CGI;
41
my $dbh = C4::Context->dbh;
42
$dbh->{AutoCommit} = 0;
43
44
my ($template, $loggedinuser, $cookie)
45
    = get_template_and_user({template_name => "test/progressbar.tt",
46
					query => $input,
47
					type => "intranet",
48
					debug => 1,
49
					});
50
51
output_html_with_http_headers $input, $cookie, $template->output;
52
53
exit 0;
54
55
(-)a/test/progressbarsubmit.pl (-104 lines)
Lines 1-104 Link Here
1
#!/usr/bin/perl
2
3
# Script for testing progressbar, part 2 - json submit handler
4
#   and Z39.50 lookups
5
6
# Koha library project  www.koha-community.org
7
8
# Licensed under the GPL
9
10
# Copyright 2010  Catalyst IT, Ltd
11
#
12
# This file is part of Koha.
13
#
14
# Koha is free software; you can redistribute it and/or modify it
15
# under the terms of the GNU General Public License as published by
16
# the Free Software Foundation; either version 3 of the License, or
17
# (at your option) any later version.
18
#
19
# Koha is distributed in the hope that it will be useful, but
20
# WITHOUT ANY WARRANTY; without even the implied warranty of
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
# GNU General Public License for more details.
23
#
24
# You should have received a copy of the GNU General Public License
25
# along with Koha; if not, see <http://www.gnu.org/licenses>.
26
27
use strict;
28
use warnings;
29
30
# standard or CPAN modules used
31
use CGI qw ( -utf8 );
32
use CGI::Cookie;
33
34
# Koha modules used
35
use C4::Context;
36
use C4::Auth;
37
use C4::Output;
38
use C4::BackgroundJob;
39
40
my $input = new CGI;
41
42
my $submitted=$input->param('submitted');
43
my $runinbackground = $input->param('runinbackground');
44
my $jobID = $input->param('jobID');
45
my $completedJobID = $input->param('completedJobID');
46
47
my ($template, $loggedinuser, $cookie)
48
    = get_template_and_user({template_name => "test/progressbar.tt",
49
                    query => $input,
50
                    type => "intranet",
51
                    debug => 1,
52
                    });
53
54
my %cookies = parse CGI::Cookie($cookie);
55
my $sessionID = $cookies{'CGISESSID'}->value;
56
if ($completedJobID) {
57
} elsif ($submitted) {
58
    my $job = undef;
59
    if ($runinbackground) {
60
        my $job_size = 100;
61
        $job = C4::BackgroundJob->new($sessionID, undef, $ENV{'SCRIPT_NAME'}, $job_size);
62
        $jobID = $job->id();
63
64
        # fork off
65
        if (my $pid = fork) {
66
            # parent
67
            # return job ID as JSON
68
            
69
            # prevent parent exiting from
70
            # destroying the kid's database handle
71
            # FIXME: according to DBI doc, this may not work for Oracle
72
73
            my $reply = CGI->new("");
74
            print $reply->header(-type => 'text/html');
75
            print '{"jobID":"' . $jobID . '"}';
76
            exit 0;
77
        } elsif (defined $pid) {
78
        # if we get here, we're a child that has detached
79
        # itself from Apache
80
81
            # close STDOUT to signal to Apache that
82
            # we're now running in the background
83
            close STDOUT;
84
            close STDERR;
85
86
            foreach (1..100) {
87
                $job->progress( $_ );
88
                sleep 1;
89
            }
90
            $job->finish();
91
        } else {
92
            # fork failed, so exit immediately
93
            die "fork failed while attempting to run $ENV{'SCRIPT_NAME'} as a background job";
94
        }
95
96
    }
97
} else {
98
    # initial form
99
    die "We should not be here";
100
}
101
102
exit 0;
103
104
(-)a/tools/upload-file-progress.pl (-63 lines)
Lines 1-62 Link Here
1
#!/usr/bin/perl
2
3
# Copyright (C) 2007 LibLime
4
#
5
# This file is part of Koha.
6
#
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.
11
#
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.
16
#
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>.
19
20
use strict;
21
#use warnings; FIXME - Bug 2505
22
23
# standard or CPAN modules used
24
use IO::File;
25
use CGI qw ( -utf8 );
26
use CGI::Session;
27
use C4::Context;
28
use C4::Auth qw/check_cookie_auth haspermission/;
29
use C4::UploadedFile;
30
use CGI::Cookie; # need to check cookies before
31
                 # having CGI parse the POST request
32
33
my $flags_required = [
34
     {circulate => 'circulate_remaining_permissions'},
35
     {tools     => 'stage_marc_import'},
36
     {tools     => 'upload_local_cover_images'},
37
];
38
39
my %cookies = fetch CGI::Cookie;
40
41
my ($auth_status, $sessionID) = check_cookie_auth($cookies{'CGISESSID'}->value);
42
43
my $auth_failure = 1;
44
foreach my $flag_required ( @{$flags_required} ) {
45
    if ( my $flags = haspermission( C4::Context->config('user'), $flag_required ) ) {
46
        $auth_failure = 0 if $auth_status eq 'ok';
47
    }
48
}
49
50
if ($auth_failure) {
51
    my $reply = CGI->new("");
52
    print $reply->header(-type => 'text/html');
53
    print '{"progress":"0"}';
54
    exit 0;
55
}
56
57
my $reported_progress = C4::UploadedFile->upload_progress($sessionID);
58
59
my $reply = CGI->new("");
60
print $reply->header(-type => 'text/html');
61
# response will be sent back as JSON
62
print '{"progress":"' . $reported_progress . '"}';
63
- 

Return to bug 14321