diff --git a/cataloguing/value_builder/upload.pl b/cataloguing/value_builder/upload.pl index 72bed7916d..e26bca7f99 100755 --- a/cataloguing/value_builder/upload.pl +++ b/cataloguing/value_builder/upload.pl @@ -4,6 +4,7 @@ # This file is part of Koha. # +# Copyright (C) 2015 Rijksmuseum # Copyright (C) 2011-2012 BibLibre # # Koha is free software; you can redistribute it and/or modify it @@ -20,145 +21,33 @@ # along with Koha; if not, see . use Modern::Perl; -use CGI qw/-utf8/; -use C4::Auth; -use C4::Context; -use C4::Output; -use C4::UploadedFiles; +# This plugin does not use the plugin launcher. It refers to tools/upload.pl. +# That script and template support using it as a plugin. + +# If the plugin is called with the pattern [id=some_hashvalue] in the +# corresponding field, it starts the upload script as a search, providing +# the possibility to delete the uploaded file. If the field is empty, you +# can upload a new file. my $builder = sub { my ( $params ) = @_; - my $function_name = $params->{id}; - my $res = " - -"; - return $res; -}; - -my $launcher = sub { - my ( $params ) = @_; - my $input = $params->{cgi}; - my $index = $input->param('index'); - my $id = $input->param('id'); - my $delete = $input->param('delete'); - my $uploaded_file = $input->param('uploaded_file'); - my $from_popup = $input->param('from_popup'); - my $isfileuploadurl = $input->param('IsFileUploadUrl'); - my $dangling = C4::UploadedFiles::DanglingEntry($id,$isfileuploadurl); - my $template_name; - if ($delete || ($id && ($dangling==0 || $dangling==1))) { - $template_name = "upload_delete_file.tt"; - } - else { - $template_name = "upload.tt"; - } - - my ( $template, $loggedinuser, $cookie ) = get_template_and_user( - { template_name => "cataloguing/value_builder/$template_name", - query => $input, - type => "intranet", - authnotrequired => 0, - flagsrequired => { editcatalogue => '*' }, - debug => 1, - } - ); - - if ($dangling==2) { - $template->param( dangling => 1 ); - } - - # Dealing with the uploaded file - my $dir = $input->param('uploadcategory'); - if ($uploaded_file and $dir) { - my $fh = $input->upload('uploaded_file'); - - $id = C4::UploadedFiles::UploadFile($uploaded_file, $dir, $fh->handle); - my $OPACBaseURL = C4::Context->preference('OPACBaseURL') // ''; - $OPACBaseURL =~ s#/$##; - if (!$OPACBaseURL) { - $template->param(MissingURL => 1); - } - if($id && $OPACBaseURL) { - my $return = "$OPACBaseURL/cgi-bin/koha/opac-retrieve-file.pl?id=$id"; - $template->param( - success => 1, - return => $return, - uploaded_file => $uploaded_file, - ); - } else { - $template->param(error => 1); - } - } elsif ($delete || ($id && ($dangling==0 || $dangling==1))) { - # If there's already a file uploaded for this field, - # We handle its deletion - if ($delete) { - if(C4::UploadedFiles::DelUploadedFile($id)==0) {; - $template->param(error => 1); + var str = document.getElementById(index).value; + var myurl, term; + if( str && str.match(/id=([0-9a-f]+)/) ) { + term = RegExp.\$1; + myurl = '../tools/upload.pl?op=search&index='+index+'&term='+term+'&plugin=1'; } else { - $template->param(success => 1); + myurl = '../tools/upload.pl?op=new&index='+index+'&plugin=1'; } + window.open( myurl, 'tag_editor', 'width=800,height=400,toolbar=false,scrollbars=yes' ); } - } else { - my $upload_path = C4::Context->config('upload_path'); - if ($upload_path) { - my $filefield = CGI::filefield( - -name => 'uploaded_file', - -size => 50, - ); - $template->param( - filefield => $filefield, - uploadcategories => C4::UploadedFiles::getCategories(), - ); - } else { - $template->param( error_upload_path_not_configured => 1 ); - } - - if (!$uploaded_file && !$dir && $from_popup) { - $template->param(error_nothing_selected => 1); - } - elsif (!$uploaded_file && $dir) { - $template->param(error_no_file_selected => 1); - } - if ($uploaded_file and not $dir) { - $template->param(error_no_dir_selected => 1); - } - - } - - $template->param( - index => $index, - id => $id, - ); - - output_html_with_http_headers $input, $cookie, $template->output; + +SCRIPT }; -return { builder => $builder, launcher => $launcher }; - -1; - -__END__ - -=head1 upload.pl - -This plugin allows to upload files on the server and reference it in a marc -field. - -It uses config variable upload_path and pref OPACBaseURL. - -=cut +return { builder => $builder }; diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/file-upload.js b/koha-tmpl/intranet-tmpl/prog/en/js/file-upload.js new file mode 100644 index 0000000000..f6817cb823 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/js/file-upload.js @@ -0,0 +1,32 @@ +function AjaxUpload ( input, progressbar, xtra, callback ) { + // input and progressbar are jQuery objects + // callback is the callback function for completion + var formData= new FormData(); + $.each( input.prop('files'), function( dx, file ) { + formData.append( "uploadfile", file ); + }); + var xhr= new XMLHttpRequest(); + var url= '/cgi-bin/koha/tools/upload-file.pl?' + xtra; + progressbar.val( 0 ); + progressbar.next('.fileuploadpercent').text( '0' ); + xhr.open('POST', url, true); + xhr.upload.onprogress = function (e) { + var p = Math.round( (e.loaded/e.total) * 100 ); + progressbar.val( p ); + progressbar.next('.fileuploadpercent').text( p ); + } + xhr.onload = function (e) { + var data = JSON.parse( xhr.responseText ); + if( data.status == 'done' ) { + progressbar.val( 100 ); + progressbar.next('.fileuploadpercent').text( '100' ); + } + callback( data.status, data.fileid, data.errors ); + } + xhr.onerror = function (e) { + // Probably only fires for network failure + alert('An error occurred while uploading.'); + } + xhr.send( formData ); + return xhr; +} diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/process_koc.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/process_koc.tt index 76c317aa52..714596fdc4 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/process_koc.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/process_koc.tt @@ -1,22 +1,43 @@ [% INCLUDE 'doc-head-open.inc' %] Koha › Circulation › Offline circulation file upload [% INCLUDE 'doc-head-close.inc' %] -[% INCLUDE 'file-upload.inc' %] + + + + + #fileuploadprogress,#jobprogress { width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; } + + @@ -90,11 +131,20 @@ function CheckForm(f) { -
+
+ + +
-
Upload progress:
0%
-
+
+
Upload progress: + + + 0% +
+
+
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-images.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-images.tt index 45659950fc..f501a90904 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-images.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-images.tt @@ -1,14 +1,40 @@ [% INCLUDE 'doc-head-open.inc' %] Koha › Tools › Upload images [% INCLUDE 'doc-head-close.inc' %] -[% INCLUDE 'file-upload.inc' %] + + + + #fileuploadprogress,#jobprogress { width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; } + + + + + + + +[% IF !plugin %] + [% INCLUDE 'header.inc' %] + [% INCLUDE 'cat-search.inc' %] + [% PROCESS breadcrumbs %] +[% END %] + +
+
+
+
+ +

Upload

+ + + +[% PROCESS submitter %] +[% IF mode == 'new' || mode == 'deleted' %] + [% PROCESS form_new %] + [% PROCESS form_search %] + [% PROCESS closer %] +[% ELSIF mode == 'report' %] + [% IF uploads %] +

Your request gave the following results:

+ [% PROCESS table_results %] + [% PROCESS closer %] + [% ELSE %] +

Sorry, your request had no results.

+ [% PROCESS newsearch %] + [% END %] +[% END %] + +
+
+
+ +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-retrieve-file.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-retrieve-file.tt new file mode 100644 index 0000000000..581d9ea114 --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-retrieve-file.tt @@ -0,0 +1,6 @@ +[%# This template is called only for a very simple error message %] + + +Your search [% IF hash %]for [% hash %][% END %] was not successful. + + diff --git a/offline_circ/enqueue_koc.pl b/offline_circ/enqueue_koc.pl index 6798f3fb8d..a81aecb406 100755 --- a/offline_circ/enqueue_koc.pl +++ b/offline_circ/enqueue_koc.pl @@ -32,7 +32,7 @@ use C4::Circulation; use C4::Items; use C4::Members; use C4::Stats; -use C4::UploadedFile; +use Koha::Upload; use Date::Calc qw( Add_Delta_Days Date_to_Days ); @@ -60,8 +60,8 @@ my $sessionID = $cookies{'CGISESSID'}->value; our $dbh = C4::Context->dbh(); if ($fileID) { - my $uploaded_file = C4::UploadedFile->fetch($sessionID, $fileID); - my $fh = $uploaded_file->fh(); + my $upload = Koha::Upload->new->get({ id => $fileID, filehandle => 1 }); + my $fh = $upload->{fh}; my @input_lines = <$fh>; my $header_line = shift @input_lines; diff --git a/offline_circ/process_koc.pl b/offline_circ/process_koc.pl index d7e5606159..21462eff63 100755 --- a/offline_circ/process_koc.pl +++ b/offline_circ/process_koc.pl @@ -32,7 +32,7 @@ use C4::Circulation; use C4::Items; use C4::Members; use C4::Stats; -use C4::UploadedFile; +use Koha::Upload; use C4::BackgroundJob; use Date::Calc qw( Add_Delta_Days Date_to_Days ); @@ -69,11 +69,11 @@ if ($completedJobID) { $template->param(transactions_loaded => 1); $template->param(messages => $results->{results}); } elsif ($fileID) { - my $uploaded_file = C4::UploadedFile->fetch($sessionID, $fileID); - my $fh = $uploaded_file->fh(); + my $upload = Koha::Upload->new->get({ id => $fileID, filehandle => 1 }); + my $fh = $upload->{fh}; + my $filename = $upload->{name}; my @input_lines = <$fh>; - my $filename = $uploaded_file->name(); my $job = undef; if ($runinbackground) { diff --git a/opac/opac-retrieve-file.pl b/opac/opac-retrieve-file.pl index 419f2bdd5c..b443a7c2fe 100755 --- a/opac/opac-retrieve-file.pl +++ b/opac/opac-retrieve-file.pl @@ -19,19 +19,33 @@ use Modern::Perl; use CGI; +use Encode; +use C4::Auth; use C4::Context; -use C4::UploadedFiles; +use C4::Output; +use Koha::Upload; -my $input = new CGI; +my $input = CGI::->new; +my $hash = $input->param('id'); # historically called id (used in URLs?) -my $id = $input->param('id'); -my $file = C4::UploadedFiles::GetUploadedFile($id); -exit 1 if !$file || !-f $file->{filepath}; - -open my $fh, '<', $file->{filepath} or die "Can't open file: $!"; -print $input->header( C4::UploadedFiles::httpheaders( $file->{filename} )); -while(<$fh>) { - print $_; +my $upl = Koha::Upload->new({ public => 1 }); +my $rec = $upl->get({ hashvalue => $hash, filehandle => 1 }); +my $fh = $rec->{fh}; +if( !$rec || !$fh ) { + my ( $template, $user, $cookie ) = get_template_and_user({ + query => $input, + template_name => 'opac-retrieve-file.tt', + type => 'opac', + authnotrequired => 1, + }); + $template->param( hash => $hash ); + output_html_with_http_headers $input, $cookie, $template->output; +} else { + my @hdr = $upl->httpheaders( $rec->{name} ); + print Encode::encode_utf8( $input->header( @hdr ) ); + while( <$fh> ) { + print $_; + } + $fh->close; } -close $fh; diff --git a/tools/stage-marc-import.pl b/tools/stage-marc-import.pl index c39b6ff052..1a7b587581 100755 --- a/tools/stage-marc-import.pl +++ b/tools/stage-marc-import.pl @@ -39,7 +39,7 @@ use C4::Output; use C4::Biblio; use C4::ImportBatch; use C4::Matcher; -use C4::UploadedFile; +use Koha::Upload; use C4::BackgroundJob; use C4::MarcModificationTemplates; use Koha::Plugins; @@ -84,8 +84,9 @@ if ($completedJobID) { my $results = $job->results(); $template->param(map { $_ => $results->{$_} } keys %{ $results }); } elsif ($fileID) { - my $uploaded_file = C4::UploadedFile->fetch($sessionID, $fileID); - my $fh = $uploaded_file->fh(); + my $upload = Koha::Upload->new->get({ id => $fileID, filehandle => 1 }); + my $fh = $upload->{fh}; + my $filename = $upload->{name}; # filename only, no path my $marcrecord=''; $/ = "\035"; while (<$fh>) { @@ -93,8 +94,8 @@ if ($completedJobID) { s/\s+$//; $marcrecord.=$_; } + $fh->close; - my $filename = $uploaded_file->name(); my $job = undef; my $dbh; if ($runinbackground) { diff --git a/tools/upload-cover-image.pl b/tools/upload-cover-image.pl index a2576ace41..79c37f25f3 100755 --- a/tools/upload-cover-image.pl +++ b/tools/upload-cover-image.pl @@ -47,7 +47,7 @@ use C4::Context; use C4::Auth; use C4::Output; use C4::Images; -use C4::UploadedFile; +use Koha::Upload; use C4::Log; my $debug = 1; @@ -83,9 +83,9 @@ $template->{VARS}->{'biblionumber'} = $biblionumber; my $total = 0; if ($fileID) { - my $uploaded_file = C4::UploadedFile->fetch( $sessionID, $fileID ); + my $upload = Koha::Upload->new->get({ id => $fileID, filehandle => 1 }); if ( $filetype eq 'image' ) { - my $fh = $uploaded_file->fh(); + my $fh = $upload->{fh}; my $srcimage = GD::Image->new($fh); if ( defined $srcimage ) { my $dberror = PutImage( $biblionumber, $srcimage, $replace ); @@ -102,7 +102,7 @@ if ($fileID) { undef $srcimage; } else { - my $filename = $uploaded_file->filename(); + my $filename = $upload->{path}; my $dirname = File::Temp::tempdir( CLEANUP => 1 ); unless ( system( "unzip", $filename, '-d', $dirname ) == 0 ) { $error = 'UZIPFAIL'; diff --git a/tools/upload-file.pl b/tools/upload-file.pl index 94a33a6baf..f54d6b1ee4 100755 --- a/tools/upload-file.pl +++ b/tools/upload-file.pl @@ -17,18 +17,17 @@ # You should have received a copy of the GNU General Public License # along with Koha; if not, see . -use strict; -#use warnings; FIXME - Bug 2505 +use Modern::Perl; -# standard or CPAN modules used -use IO::File; use CGI qw ( -utf8 ); -use CGI::Session; +use CGI::Cookie; +use Encode; +use JSON; +use URI::Escape; + use C4::Context; use C4::Auth qw/check_cookie_auth haspermission/; -use CGI::Cookie; # need to check cookies before - # having CGI parse the POST request -use C4::UploadedFile; +use Koha::Upload; # upload-file.pl must authenticate the user # before processing the POST request, @@ -36,7 +35,7 @@ use C4::UploadedFile; # not authorized. Consequently, unlike # most of the other CGI scripts, upload-file.pl # requires that the session cookie already -# have been created. +# has been created. my $flags_required = [ {circulate => 'circulate_remaining_permissions'}, @@ -44,52 +43,53 @@ my $flags_required = [ {tools => 'upload_local_cover_images'} ]; -my %cookies = fetch CGI::Cookie; +my %cookies = CGI::Cookie->fetch; +my $sid = $cookies{'CGISESSID'}->value; my $auth_failure = 1; -my ( $auth_status, $sessionID ) = check_cookie_auth( $cookies{'CGISESSID'}->value ); +my ( $auth_status, $sessionID ) = check_cookie_auth( $sid ); +my $uid = C4::Auth::get_session($sid)->param('id'); foreach my $flag_required ( @{$flags_required} ) { - if ( my $flags = haspermission( C4::Context->config('user'), $flag_required ) ) { + if ( my $flags = haspermission( $uid, $flag_required ) ) { $auth_failure = 0 if $auth_status eq 'ok'; } } if ($auth_failure) { - $auth_status = 'denied' if $auth_status eq 'failed'; - send_reply($auth_status, ""); + send_reply( 'denied' ); exit 0; } -our $uploaded_file = C4::UploadedFile->new($sessionID); -unless (defined $uploaded_file) { - # FIXME - failed to create file for some reason - send_reply('failed', ''); - exit 0; +my $upload = Koha::Upload->new( upload_pars($ENV{QUERY_STRING}) ); +if( !$upload || !$upload->cgi || !$upload->count ) { + # not one upload succeeded + send_reply( 'failed', undef, $upload? $upload->err: undef ); +} else { + # in case of multiple uploads, at least one got through + send_reply( 'done', $upload->result, $upload->err ); } -$uploaded_file->max_size($ENV{'CONTENT_LENGTH'}); # may not be the file size, exactly - -my $query; -$query = new CGI \&upload_hook; -$uploaded_file->done(); -send_reply('done', $uploaded_file->id()); - -# FIXME - if possible, trap signal caused by user cancelling upload -# FIXME - something is wrong during cleanup: \t(in cleanup) Can't call method "commit" on unblessed reference at /usr/local/share/perl/5.8.8/CGI/Session/Driver/DBI.pm line 130 during global destruction. exit 0; -sub upload_hook { - my ($file_name, $buffer, $bytes_read, $session) = @_; - $uploaded_file->stash(\$buffer, $bytes_read); - if ( ! $uploaded_file->name && $file_name ) { # save name on first chunk - $uploaded_file->name($file_name); - } -} - -sub send_reply { - my ($upload_status, $fileid) = @_; - +sub send_reply { # response will be sent back as JSON + my ( $upload_status, $data, $error ) = @_; my $reply = CGI->new(""); - print $reply->header(-type => 'text/html'); - # response will be sent back as JSON - print '{"status":"' . $upload_status . '","fileid":"' . $fileid . '"}'; + print $reply->header( -type => 'text/html', -charset => 'UTF-8' ); + print JSON::encode_json({ + status => $upload_status, + fileid => $data, + errors => $error, + }); +} + +sub upload_pars { + my ( $qstr ) = @_; + $qstr = Encode::decode_utf8( uri_unescape( $qstr ) ); + # category could include a utf8 character + my $rv = {}; + foreach my $p ( qw[public category temp] ) { + if( $qstr =~ /(^|&)$p=(\w+)(&|$)/ ) { + $rv->{$p} = $2; + } + } + return $rv; } diff --git a/tools/upload.pl b/tools/upload.pl new file mode 100755 index 0000000000..5d9c8e7719 --- /dev/null +++ b/tools/upload.pl @@ -0,0 +1,101 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Copyright (C) 2015 Rijksmuseum +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw/-utf8/; +use JSON; + +use C4::Auth; +use C4::Output; +use Koha::Upload; + +my $input = CGI::->new; +my $op = $input->param('op') // 'new'; +my $plugin = $input->param('plugin'); +my $index = $input->param('index'); # MARC editor input field id +my $term = $input->param('term'); +my $id = $input->param('id'); +my $msg = $input->param('msg'); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { template_name => "tools/upload.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { editcatalogue => '*' }, + } +); + +$template->param( + plugin => $plugin, + index => $index, +); + +my $upar = $plugin ? { public => 1 } : {}; +if ( $op eq 'new' ) { + $template->param( + mode => 'new', + uploadcategories => Koha::Upload->getCategories, + ); + output_html_with_http_headers $input, $cookie, $template->output; +} elsif ( $op eq 'search' ) { + my $h = $id ? { id => $id } : { term => $term }; + my @uploads = Koha::Upload->new($upar)->get($h); + $template->param( + mode => 'report', + msg => $msg, + uploads => \@uploads, + ); + output_html_with_http_headers $input, $cookie, $template->output; +} elsif ( $op eq 'delete' ) { + + # delete only takes the id parameter + my $upl = Koha::Upload->new($upar); + my ($fn) = $upl->delete( { id => $id } ); + my $e = $upl->err; + my $msg = + $fn ? JSON::to_json( { $fn => 6 } ) + : $e ? JSON::to_json($e) + : undef; + $template->param( + mode => 'deleted', + msg => $msg, + uploadcategories => $upl->getCategories, + ); + output_html_with_http_headers $input, $cookie, $template->output; +} elsif ( $op eq 'download' ) { + my $upl = Koha::Upload->new($upar); + my $rec = $upl->get( { id => $id, filehandle => 1 } ); + my $fh = $rec->{fh}; + if ( !$rec || !$fh ) { + $template->param( + mode => 'new', + msg => JSON::to_json( { $id => 5 } ), + uploadcategories => $upl->getCategories, + ); + output_html_with_http_headers $input, $cookie, $template->output; + } else { + my @hdr = $upl->httpheaders( $rec->{name} ); + print Encode::encode_utf8( $input->header(@hdr) ); + while (<$fh>) { + print $_; + } + $fh->close; + } +}