Browse Source

Bug 14321: Integrate Upload.pm into Koha

This patch makes the following changes to achieve that:
[1] Use Upload.pm in stage-marc-import.pl, upload-cover-image.pl,
    offline_circ/process_koc.pl and enqueue_koc.pl.
[2] A new file-upload.js replaces file-upload.inc in the associated template.
    We now use ajax to get progress figures instead of launching perl script
    upload-file-progress.
    The js changes now also allow for aborting a file upload.
[3] Adds a tools/upload script and template. It allows to upload multiple
    files at once.
[4] Makes upload-file return error messages in JSON. For a multiple upload,
    we could have some files with errors and others without errors.
    The upload is now marked as Failed only if there was no upload at all.
[5] The upload plugin is converted to use tools/upload with plugin param.
    Deleting an upload is now presented via the search results form.

NOTE: In editing the process_koc.tt I noticed that the form enqueuefile was
hidden and no longer used (with associated code in process_koc.pl). When a
file has been uploaded, I display the form again (with the Apply directly
button). The code still works.

NOTE: We fix an error in upload-file from one of the patches of bug 6874.
The userid of the Koha admin user is passed to haspermission, but we
should pick the userid from the session.

NOTE: Bug 14686 will add a specific permission for tools/upload.pl, and
will add the tools/upload script to the Tools menu.
For now, you need edit_catalogue to start upload.pl and you will
additionally need a permission like upload_local_cover_images
to successfully upload a new file.

Test plan:
[1] Upload a marc file in stage-marc-import. (This is temp storage.)
[2] Check new entry in table uploaded_files. Look for the file in your
    temporary directory (/tmp ?), subfolder koha_upload.
    Bonus: Remove permissions on this subfolder. Retry, check error and
    restore permissions again.
[3] Upload another (larger) file and abort the upload. Check table and
    directory again. You should have a partial file, but no record.
[4] Verify that Stage for import still works as expected.
[5] Test Upload local cover image. (Enable OPACLocalCoverImages.) You can
    test an individual image or a zip file including images and a file
    called datalink.txt (with lines biblionumber,filename).
[6] Test uploading a offline circulation file:
    Enable AllowOfflineCirculation, and create a koc file (plain text):
    Line1: Version=1.0\tA=1\tB=2
    Line2: 2015-08-06 08:00:00 345\treturn\t[barcode]
    Note: Replace tabs and barcode. The number of tabs is essential!
    Checkout the item with your barcode.
    Go to Offline circulation file upload. Upload and click Apply directly.
    Checkout again. Upload again, click Add to offline circulation queue.
[7] Upload three files via tools/upload.pl with a category and marked as
    public. Check the results in the table.
    Verify that you can download the file in OPAC without being logged in.
[8] Pick one new file and one of the files of step 7. Upload them in the
    same category. One upload should succeed. Check for reported error.
[9] Connect upload.pl to field 856$u.
    Goto Cataloguing editor.
    In an empty 856$u, click the tag editor. Upload a file and click Choose.
    Save the record. Open the record in the OPAC and click the link.
    Copy this link to your clipboard for next step.
[A] Go back to editor. Click the tag editor on the same 856 field.
    Choose for Delete.
    Open the link in your clipboard again. Error message?
[B] Check the process of upload, search, download and delete of an upload
    with some diacritical characters in the filename. (Bonus points for
    adding special chars in the category code.)
    Note: You can add categories via authorized values, UPLOAD key.

Signed-off-by: Mirko Tietgen <mirko@abunchofthings.net>
Signed-off-by: Julian Maurice <julian.maurice@biblibre.com>

Bug 14321: [QA Follow-up] Perltidy upload.pl

Run perltidy -pro=xt/perltidyrc on tools/upload.pl.
No other changes.

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Julian Maurice <julian.maurice@biblibre.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
3.22.x
Marcel de Rooy 9 years ago
committed by Tomas Cohen Arazi
parent
commit
83ea49a009
  1. 153
      cataloguing/value_builder/upload.pl
  2. 32
      koha-tmpl/intranet-tmpl/prog/en/js/file-upload.js
  3. 39
      koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/process_koc.tt
  4. 64
      koha-tmpl/intranet-tmpl/prog/en/modules/tools/stage-marc-import.tt
  5. 45
      koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-images.tt
  6. 342
      koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload.tt
  7. 6
      koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-retrieve-file.tt
  8. 6
      offline_circ/enqueue_koc.pl
  9. 8
      offline_circ/process_koc.pl
  10. 36
      opac/opac-retrieve-file.pl
  11. 9
      tools/stage-marc-import.pl
  12. 8
      tools/upload-cover-image.pl
  13. 82
      tools/upload-file.pl
  14. 101
      tools/upload.pl

153
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 <http://www.gnu.org/licenses>.
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 = "
<script type=\"text/javascript\">
function Click$function_name(event) {
return <<"SCRIPT";
<script type=\"text/javascript\">
function Click$params->{id}(event) {
var index = event.data.id;
var id = document.getElementById(index).value;
var IsFileUploadUrl=0;
if (id.match(/opac-retrieve-file/)) {
IsFileUploadUrl=1;
}
if(id.match(/id=([0-9a-f]+)/)){
id = RegExp.\$1;
}
var newin=window.open(\"../cataloguing/plugin_launcher.pl?plugin_name=upload.pl&index=\"+index+\"&id=\"+id+\"&from_popup=0\"+\"&IsFileUploadUrl=\"+IsFileUploadUrl, 'upload', 'width=600,height=400,toolbar=false,scrollbars=no');
newin.focus();
}
</script>
";
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>
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 };

32
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;
}

39
koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/process_koc.tt

@ -1,22 +1,43 @@
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha &rsaquo; Circulation &rsaquo; Offline circulation file upload</title>
[% INCLUDE 'doc-head-close.inc' %]
[% INCLUDE 'file-upload.inc' %]
<script type="text/javascript" src="[% themelang %]/js/background-job-progressbar.js"></script>
<script type="text/javascript" src="[% themelang %]/js/file-upload.js"></script>
<script type="text/javascript">
//<![CDATA[
var xhr;
$(document).ready(function(){
$("#enqueuefile").hide();
$("#processfile").hide();
});
function CheckUpload(f){
if (f.fileToUpload.value == ""){
alert(_("Please choose a file to upload"));
function StartUpload() {
if( $('#fileToUpload').prop('files').length == 0 ) return;
$('#fileuploadform input.submit').prop('disabled',true);
$("#fileuploadfailed").hide();
$("#processfile").hide();
$("#fileuploadstatus").show();
$("form#processfile #uploadedfileid").val('');
$("form#enqueuefile #uploadedfileid").val('');
xhr= AjaxUpload( $('#fileToUpload'), $('#fileuploadprogress'), 'temp=1', cbUpload );
}
function cbUpload( status, fileid ) {
if( status=='done' ) {
$("form#processfile #uploadedfileid").val( fileid );
$("form#enqueuefile #uploadedfileid").val( fileid );
$('#fileToUpload').prop('disabled',true);
$("#processfile").show();
$("#enqueuefile").show();
} else {
return ajaxFileUpload()
$("#fileuploadstatus").hide();
$("#fileuploadfailed").show();
$("#fileuploadfailed").text( _("Upload status: ") +
( status=='failed'? _("Failed"):
( status=='denied'? _("Denied"): status ))
);
}
return false;
}
function CheckForm(f) {
@ -25,6 +46,8 @@ function CheckForm(f) {
} else {
$("#fileuploadstatus").hide();
$("#fileuploadform").slideUp();
$("#mainformsubmit").prop('disabled',true);
$("#queueformsubmit").prop('disabled',true);
return submitBackgroundJob(f);
}
return false;
@ -70,10 +93,10 @@ function CheckForm(f) {
<fieldset class="brief">
<ol><li><label for="fileToUpload">Choose .koc file: </label>
<input type="file" id="fileToUpload" size="50" name="fileToUpload" /></li></ol>
<fieldset class="action"><input type="button" class="submit" value="Upload file" onclick="CheckUpload(this.form);" /></fieldset>
<fieldset class="action"><input type="button" class="submit" value="Upload file" onclick="StartUpload();return false;" /></fieldset>
</fieldset>
</form>
<div id="fileuploadstatus" style="display:none">Upload progress: <div id="fileuploadprogress"></div> <span id="fileuploadpercent">0</span>%</div>
<div id="fileuploadstatus" style="display:none">Upload progress: <progress id="fileuploadprogress" min="0" max="100" value="0"></progress> <span class="fileuploadpercent">0</span>%</div>
<div id="fileuploadfailed" style="display:none"></div>
</div>

64
koha-tmpl/intranet-tmpl/prog/en/modules/tools/stage-marc-import.tt

@ -1,14 +1,19 @@
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha &rsaquo; Tools &rsaquo; Stage MARC records for import</title>
[% INCLUDE 'doc-head-close.inc' %]
[% INCLUDE 'file-upload.inc' %]
<script type="text/javascript" src="[% themelang %]/js/background-job-progressbar.js"></script>
<script type="text/javascript" src="[% themelang %]/js/file-upload.js"></script>
<style type="text/css">
#uploadpanel,#fileuploadstatus,#fileuploadfailed,#jobpanel,#jobstatus,#jobfailed { display : none; }
#fileuploadstatus,#fileuploadfailed,#fileuploadcancel,#jobpanel,#jobstatus,#jobfailed { display : none; }
#fileuploadstatus,#jobstatus { margin:.4em; }
#fileuploadprogress,#jobprogress{ width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; }</style>
#fileuploadprogress,#jobprogress { width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; }
</style>
<script type="text/javascript">
//<![CDATA[
var xhr;
$(document).ready(function(){
$("#processfile").hide();
$("#record_type").change(function() {
@ -27,7 +32,43 @@ function CheckForm(f) {
}
return false;
}
function StartUpload() {
if( $('#fileToUpload').prop('files').length == 0 ) return;
$('#fileuploadbutton').hide();
$("#fileuploadfailed").hide();
$("#processfile").hide();
$("#fileuploadstatus").show();
$("#uploadedfileid").val('');
xhr= AjaxUpload( $('#fileToUpload'), $('#fileuploadprogress'), 'temp=1', cbUpload );
$("#fileuploadcancel").show();
}
function CancelUpload() {
if( xhr ) xhr.abort();
$("#fileuploadstatus").hide();
$('#fileuploadbutton').show();
$("#fileuploadcancel").hide();
$("#fileuploadfailed").show();
$("#fileuploadfailed").text( _("Upload status: Cancelled ") );
}
function cbUpload( status, fileid ) {
if( status=='done' ) {
$("#uploadedfileid").val( fileid );
$('#fileToUpload').prop('disabled',true);
$('#fileuploadbutton').prop('disabled',true);
$('#fileuploadbutton').show();
$("#fileuploadcancel").hide();
$("#processfile").show();
} else {
$('#fileuploadbutton').show();
$("#fileuploadcancel").hide();
$("#fileuploadstatus").hide();
$("#fileuploadfailed").show();
$("#fileuploadfailed").text( _("Upload status: ") +
( status=='failed'? _("Failed"):
( status=='denied'? _("Denied"): status ))
);
}
}
//]]>
</script>
</head>
@ -90,11 +131,20 @@ function CheckForm(f) {
<input type="file" id="fileToUpload" name="fileToUpload" />
</div> </li>
</ol>
<fieldset class="action"><button class="submit" onclick="return ajaxFileUpload();">Upload file</button></fieldset>
<fieldset class="action">
<button id="fileuploadbutton" onclick="StartUpload(); return false;">Upload file</button>
<button id="fileuploadcancel" onclick="CancelUpload(); return false;">Cancel</button>
</fieldset>
</fieldset>
<div id="uploadpanel"><div id="fileuploadstatus">Upload progress: <div id="fileuploadprogress"></div> <span id="fileuploadpercent">0</span>%</div>
<div id="fileuploadfailed"></div></div>
<div id="fileuploadpanel">
<div id="fileuploadstatus">Upload progress:
<progress id="fileuploadprogress" min="0" max="100" value="0">
</progress>
<span class="fileuploadpercent">0</span>%
</div>
<div id="fileuploadfailed"></div>
</div>
</form>
<form method="post" id="processfile" action="[% SCRIPT_NAME %]" enctype="multipart/form-data">

45
koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-images.tt

@ -1,14 +1,40 @@
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha &rsaquo; Tools &rsaquo; Upload images</title>
[% INCLUDE 'doc-head-close.inc' %]
[% INCLUDE 'file-upload.inc' %]
<script type="text/javascript" src="[% themelang %]/js/background-job-progressbar.js"></script>
<script type="text/javascript" src="[% themelang %]/js/file-upload.js"></script>
<style type="text/css">
#uploadpanel,#fileuploadstatus,#fileuploadfailed,#jobpanel,#jobstatus,#jobfailed { display : none; }
#fileuploadstatus,#fileuploadfailed,#jobpanel,#jobstatus,#jobfailed { display : none; }
#fileuploadstatus,#jobstatus { margin:.4em; }
#fileuploadprogress,#jobprogress{ width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; }</style>
#fileuploadprogress,#jobprogress { width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; }
</style>
<script type="text/javascript">
//<![CDATA[
function StartUpload() {
if( $('#fileToUpload').prop('files').length == 0 ) return;
$('#uploadform button.submit').prop('disabled',true);
$("#fileuploadstatus").show();
$("#uploadedfileid").val('');
xhr= AjaxUpload( $('#fileToUpload'), $('#fileuploadprogress'), 'temp=1', cbUpload );
}
function cbUpload( status, fileid ) {
if( status=='done' ) {
$("#uploadedfileid").val( fileid );
$('#fileToUpload').prop('disabled',true);
$("#processfile").show();
} else {
$("#fileuploadstatus").hide();
$("#fileuploadfailed").show();
$("#fileuploadfailed").text( _("Upload status: ") +
( status=='failed'? _("Failed"):
( status=='denied'? _("Denied"): status ))
);
$("#processfile").hide();
}
}
$(document).ready(function(){
$("#processfile").hide();
$("#zipfile").click(function(){
@ -19,7 +45,8 @@ $(document).ready(function(){
});
$("#uploadfile").validate({
submitHandler: function(form) {
ajaxFileUpload();
StartUpload();
return false;
}
});
});
@ -76,8 +103,14 @@ $(document).ready(function(){
<fieldset class="action"><button class="submit">Upload file</button></fieldset>
</fieldset>
<div id="uploadpanel"><div id="fileuploadstatus">Upload progress: <div id="fileuploadprogress"></div> <span id="fileuploadpercent">0</span>%</div>
<div id="fileuploadfailed"></div></div>
<div id="uploadpanel">
<div id="fileuploadstatus">Upload progress:
<progress min="0" max="100" value="0" id="fileuploadprogress">
</progress>
<span class="fileuploadpercent">0</span>%
</div>
<div id="fileuploadfailed"></div>
</div>
</form>
<form method="post" id="processfile" action="[% SCRIPT_NAME %]" enctype="multipart/form-data">

342
koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload.tt

@ -0,0 +1,342 @@
[% INCLUDE 'doc-head-open.inc' %]
[% USE Koha %]
[% IF plugin %]
<title>Upload plugin</title>
[% ELSE %]
<title>Koha &rsaquo; Tools &rsaquo; Upload</title>
[% END %]
[% INCLUDE 'doc-head-close.inc' %]
[% BLOCK plugin_pars %]
[% IF plugin %]
<input type="hidden" name="plugin" value="1" />
<input type="hidden" name="index" value="[% index %]" />
[% END %]
[% END %]
[% BLOCK breadcrumbs %]
<div id="breadcrumbs">
<a href="/cgi-bin/koha/mainpage.pl">Home</a>
&rsaquo;
<a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a>
&rsaquo;
<a href="/cgi-bin/koha/tools/upload.pl">Upload</a>
&rsaquo;
<span id="lastbreadcrumb">
[% IF mode=='new' || mode =='deleted'%]
Add new upload or search
[% ELSE %]
Results
[% END %]
</span>
</div>
[% END %]
[% BLOCK form_new %]
<form method="post" action="[% SCRIPT_NAME %]" id="uploadfile" enctype="multipart/form-data">
[% PROCESS plugin_pars %]
<fieldset class="rows" id="uploadform">
<legend>Upload new files</legend>
<ol>
<li>
<div id="fileuploadform">
<label for="fileToUpload">Select files: </label>
<input type="file" id="fileToUpload" name="fileToUpload" multiple/>
</div>
</li>
[% IF uploadcategories %]
<li>
<label for="uploadcategory">Category: </label>
<select id="uploadcategory" name="uploadcategory">
[% IF !plugin %]
<option value="" disabled hidden selected></option>
[% END %]
[% FOREACH cat IN uploadcategories %]
<option value="[% cat.code %]">[% cat.name %]</option>
[% END %]
</select>
</li>
[% END %]
[% IF !plugin %]
<li>
[% IF uploadcategories %]
<div class="hint">Note: For temporary uploads do not select a category.</div>
[% ELSE %]
<div class="hint">Note: No upload categories have been defined. All uploads will be marked as temporary.</div>
[% END %]
</li>
[% END %]
<li>
[% IF plugin %]
<input type="hidden" id="public" name="public" value="1"/>
[% ELSE %]
<label>&nbsp;</label>
<input type="checkbox" id="public" name="public">
Allow public downloads
</input>
[% END %]
</li>
</ol>
<fieldset class="action">
<button id="fileuploadbutton" onclick="StartUpload(); return false;">Upload</button>
<button id="fileuploadcancel" onclick="CancelUpload(); return false;">Cancel</button>
</fieldset>
</fieldset>
<div id="fileuploadpanel">
<div id="fileuploadstatus">Upload progress:
<progress id="fileuploadprogress" min="0" max="100" value="0">
</progress>
<span class="fileuploadpercent">0</span>%
</div>
<div id="fileuploadfailed"></div>
</div>
</form>
[% END %]
[% BLOCK form_search %]
<form method="post" id="searchfile" action="[% SCRIPT_NAME %]" enctype="multipart/form-data">
[% PROCESS plugin_pars %]
<input type="hidden" name="op" value="search"/>
<fieldset class="rows">
<legend>Search uploads by name or hashvalue</legend>
<ol>
<li>
<label for="searchupload">Search term: </label>
<input type="text" id="term" name="term" value=""/>
</li>
<li>
<fieldset class="action">
<button id="searchbutton" onclick="return CheckSearch();" class="submit">Search</button>
</fieldset>
</li>
</ol>
</fieldset>
</form>
[% END %]
[% BLOCK submitter %]
<form id="submitter" style="display:none;" method="post">
[% PROCESS plugin_pars %]
<input type="hidden" name="op" id="op" value=""/>
<input type="hidden" name="id" id="id" value="" />
<input type="hidden" name="msg" id="msg" value="" />
</form>
[% END %]
[% BLOCK closer %]
[% IF plugin %]
<form id="closer">
<fieldset class="action">
<button onclick="window.close();return false;">Close</button>
</fieldset>
</form>
[% END %]
[% END %]
[% BLOCK newsearch %]
<form id="newsearch">
<fieldset class="action">
<button onclick="SubmitMe('new'); return false;">New search</button>
[% IF plugin %]
<button onclick="window.close();return false;">Close</button>
[% END %]
</fieldset>
</form>
[% END %]
[% BLOCK table_results %]
<table>
<thead>
<tr>
<th>Filename</td>
<th>Size</td>
<th>Hashvalue</td>
<th>Category</td>
[% IF !plugin %]<th>Public</td>[% END %]
[% IF !plugin %]<th>Temporary</td>[% END %]
<th>Actions</td>
</tr>
</thead>
<tbody>
[% FOREACH record IN uploads %]
<tr>
<td>[% record.name %]</td>
<td>[% record.filesize %]</td>
<td>[% record.hashvalue %]</td>
<td>[% record.categorycode %]</td>
[% IF !plugin %]
<td>[% IF record.public %]Yes[% ELSE %]No[% END %]</td>
<td>[% IF record.permanent %]No[% ELSE %]Yes[% END %]</td>
[% END %]
<td>
[% IF plugin %]
<a href="" onclick="Choose('[% record.hashvalue %]'); return false;">Choose</a>&nbsp;
[% END %]
<a href="" onclick="SubmitMe( 'download', [% record.id %] ); return false;">Download</a>&nbsp;
<a href="" onclick="ClearField(); SubmitMe( 'delete', [% record.id %] ); return false;">Delete</a>
</td>
</tr>
[% END %]
</tbody>
</table>
[% END %]
<style type="text/css">
#fileuploadstatus,#fileuploadfailed { display : none; }
#fileuploadstatus { margin:.4em; }
#fileuploadprogress { width:150px;height:10px;border:1px solid #666;background:url('[% interface %]/[% theme %]/img/progress.png') -300px 0px no-repeat; }
</style>
<script type="text/javascript">
//<![CDATA[
var errMESSAGES = [
"Error 0: Not in use",
_("This file already exists (in this category)."),
_("File could not be created. Check permissions."),
_("Your koha-conf.xml does not contain a valid upload_path."),
_("No temporary directory found."),
_("File could not be read."),
_("File has been deleted."),
_("File could not be deleted."),
];
//]]>
</script>
<script type="text/javascript" src="[% themelang %]/js/file-upload.js"></script>
<script type="text/javascript">
//<![CDATA[
function StartUpload() {
if( $('#fileToUpload').prop('files').length == 0 ) return;
$('#fileToUpload').prop('disabled',true);
$('#fileuploadbutton').hide();
$("#fileuploadcancel").show();
$("#fileuploadfailed").html('');
$("#myalerts").hide('');
$("#myalerts").html('');
$("#fileuploadstatus").show();
$("#uploadedfileid").val('');
$("#searchfile").hide();
$("#lastbreadcrumb").text( _("Add a new upload") );
var cat, xtra='';
if( $("#uploadcategory").val() )
cat = encodeURIComponent( $("#uploadcategory").val() );
if( cat ) xtra= 'category=' + cat + '&';
[% IF plugin %]
xtra = xtra + 'public=1&temp=0';
[% ELSE %]
if( !cat ) xtra = 'temp=1&';
if( $('#public').prop('checked') ) xtra = xtra + 'public=1';
[% END %]
xhr= AjaxUpload( $('#fileToUpload'), $('#fileuploadprogress'), xtra, cbUpload );
}
function CancelUpload() {
if( xhr ) xhr.abort();
$("#fileuploadstatus").hide();
$('#fileToUpload').prop('disabled', false);
$('#fileuploadbutton').show();
$("#fileuploadcancel").hide();
$("#fileuploadfailed").show();
$("#fileuploadfailed").text( _("Upload status: Cancelled ") );
}
function cbUpload( status, fileid, err ) {
$('#fileToUpload').prop('disabled', false);
if( status=='done' ) {
var e = err? JSON.stringify(err): '';
SubmitMe( 'search', fileid, e );
} else {
$('#fileuploadbutton').show();
$("#fileuploadcancel").hide();
$("#fileuploadstatus").hide();
$("#fileuploadfailed").show();
$("#fileuploadfailed").html( _("Upload status: ") +
( status=='failed'? _("Failed"):
( status=='denied'? _("Denied"): status ))
);
ShowAlerts( err );
}
}
function ShowAlerts(err) {
var str = '';
for( var file in err ) {
str= str + '<p>' + file + ': ' +
errMESSAGES[ err[file] ] + '</p>';
}
if( str ) {
$('#myalerts').html(str);
$('#myalerts').show();
}
}
function CheckSearch() {
if( $("#term").val()=="" ) {
alert( _("Please enter a search term.") );
return false;
}
return true;
}
function SubmitMe(op, id, msg ) {
$("#submitter #op").val( op );
$("#submitter #id").val( id );
$("#submitter #msg").val( msg );
$("#submitter").submit();
}
function ClearField() {
[% IF plugin %]
$(window.opener.document).find('#[% index %]').val( '' );
[% END %]
}
function Choose(hashval) {
var res = '[% Koha.Preference('OPACBaseURL') %]';
res = res.replace( /\/$/, '');
res = res + '/cgi-bin/koha/opac-retrieve-file.pl?id=' + hashval;
[% IF index %]
$(window.opener.document).find('#[% index %]').val( res );
[% END %]
window.close();
}
$(document).ready(function() {
[% IF msg %]
ShowAlerts( [% msg %] );
[% END %]
$("#fileuploadcancel").hide();
});
//]]>
</script>
</head>
<body id="tools_upload" class="tools">
[% IF !plugin %]
[% INCLUDE 'header.inc' %]
[% INCLUDE 'cat-search.inc' %]
[% PROCESS breadcrumbs %]
[% END %]
<div id="doc3" class="yui-t2">
<div id="bd">
<div id="yui-main">
<div class="yui-b">
<h1>Upload</h1>
<div class="dialog alert" id="myalerts" style="display:none;"></div>
[% PROCESS submitter %]
[% IF mode == 'new' || mode == 'deleted' %]
[% PROCESS form_new %]
[% PROCESS form_search %]
[% PROCESS closer %]
[% ELSIF mode == 'report' %]
[% IF uploads %]
<h3>Your request gave the following results:</h3>
[% PROCESS table_results %]
[% PROCESS closer %]
[% ELSE %]
<h4>Sorry, your request had no results.</h4>
[% PROCESS newsearch %]
[% END %]
[% END %]
</div>
</div>
</div>
[% INCLUDE 'intranet-bottom.inc' %]

6
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 %]
<html>
<body>
Your search [% IF hash %]for [% hash %][% END %] was not successful.
</body>
</html>

6
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;

8
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) {

36
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;

9
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) {

8
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';

82
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 <http://www.gnu.org/licenses>.
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 { # response will be sent back as JSON
my ( $upload_status, $data, $error ) = @_;
my $reply = CGI->new("");
print $reply->header( -type => 'text/html', -charset => 'UTF-8' );
print JSON::encode_json({
status => $upload_status,
fileid => $data,
errors => $error,
});
}
sub send_reply {
my ($upload_status, $fileid) = @_;
my $reply = CGI->new("");
print $reply->header(-type => 'text/html');
# response will be sent back as JSON
print '{"status":"' . $upload_status . '","fileid":"' . $fileid . '"}';
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;
}

101
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 <http://www.gnu.org/licenses>.
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;
}
}
Loading…
Cancel
Save