WebClient: add drag and drop upload UI

thanks to @wooneusean for the help

Fixes #951

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-11-19 12:31:03 +01:00
parent 29d1993a3b
commit 6ebe7691db
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
4 changed files with 178 additions and 10 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<link href="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.css" rel="stylesheet"> <link href="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/lightbox2/css/lightbox.min.css" rel="stylesheet"> <link href="{{.StaticURL}}/vendor/lightbox2/css/lightbox.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/video-js/video-js.min.css" rel="stylesheet" /> <link href="{{.StaticURL}}/vendor/video-js/video-js.min.css" rel="stylesheet" />
<link href="{{.StaticURL}}/vendor/filepond/filepond.min.css" rel="stylesheet" />
<style> <style>
div.dataTables_wrapper span.selected-info, div.dataTables_wrapper span.selected-info,
div.dataTables_wrapper span.selected-item { div.dataTables_wrapper span.selected-item {
@ -48,7 +49,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div class="card-body text-form-error">{{.Error}}</div> <div class="card-body text-form-error">{{.Error}}</div>
</div> </div>
{{end}} {{end}}
<div class="table-responsive"> <div id="tableContainer" class="table-responsive">
<table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0"> <table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0">
<thead> <thead>
<tr> <tr>
@ -110,7 +111,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
<form id="upload_files_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data"> <form id="upload_files_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data">
<div class="modal-body"> <div class="modal-body">
<input type="file" class="form-control-file" id="files_name" name="filenames" required multiple> <div id="uploadErrorMsg" class="card mb-4 border-left-warning" style="display: none;">
<div id="uploadErrorTxt" class="card-body text-form-error"></div>
</div>
<input type="file" id="files_name" name="filenames" required multiple>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<input type="hidden" name="_form_token" value="{{.CSRFToken}}"> <input type="hidden" name="_form_token" value="{{.CSRFToken}}">
@ -229,6 +233,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<script src="{{.StaticURL}}/vendor/codemirror/codemirror.js"></script> <script src="{{.StaticURL}}/vendor/codemirror/codemirror.js"></script>
<script src="{{.StaticURL}}/vendor/codemirror/meta.js"></script> <script src="{{.StaticURL}}/vendor/codemirror/meta.js"></script>
<script src="{{.StaticURL}}/vendor/video-js/video.min.js"></script> <script src="{{.StaticURL}}/vendor/video-js/video.min.js"></script>
<script src="{{.StaticURL}}/vendor/filepond/filepond.min.js"></script>
{{if .HasIntegrations}} {{if .HasIntegrations}}
<script type="text/javascript"> <script type="text/javascript">
var childReference = null; var childReference = null;
@ -530,6 +535,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
return "far fa-file-code"; return "far fa-file-code";
case "zip": case "zip":
case "zipx": case "zipx":
case "7z":
case "rar": case "rar":
case "tar": case "tar":
case "gz": case "gz":
@ -652,6 +658,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
}); });
} }
const isDirectoryEntry = item => isEntry(item) && (getAsEntry(item) || {}).isDirectory;
const isEntry = item => 'webkitGetAsEntry' in item;
const getAsEntry = item => item.webkitGetAsEntry();
$(document).ready(function () { $(document).ready(function () {
player = videojs('video_player', { player = videojs('video_player', {
controls: true, controls: true,
@ -671,6 +681,72 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
$('#spinnerModal').modal('hide'); $('#spinnerModal').modal('hide');
} }
}); });
{{if .CanAddFiles}}
FilePond.create(document.getElementById("files_name"),{
allowMultiple: true,
name: 'filenames',
maxFiles: 30,
credits: false,
required: true,
onwarning: function(error){
if (error.code == 0){
$('#uploadErrorTxt').text('You can upload a maximum of 30 files');
$('#uploadErrorMsg').show();
setTimeout(function () {
$('#uploadErrorMsg').hide();
}, 10000);
}
},
beforeAddFile: (fileItem) => new Promise(resolve => {
let num = 0;
FilePond.find(document.getElementById("files_name")).getFiles().forEach(function(val){
if (val.filename == fileItem.filename){
num++;
}
});
resolve(num == 1);
})
});
$('#tableContainer').on("dragover", function(ev){
ev.preventDefault();
$('#tableContainer').css('opacity','0.5');
});
$('#tableContainer').on("dragend dragleave", function(ev){
ev.preventDefault();
$('#tableContainer').css('opacity','1');
});
$('#tableContainer').on("drop", function(ev){
ev.preventDefault();
$('#tableContainer').css('opacity','1');
let filesDropped = false;
if (ev.originalEvent.dataTransfer.items) {
[...ev.originalEvent.dataTransfer.items].forEach((item, i) => {
if (item.kind === 'file') {
// if this is a directory just open the upload dialog
if (!isDirectoryEntry(item)){
FilePond.find(document.getElementById("files_name")).addFile(item.getAsFile());
}
filesDropped = true;
}
});
} else {
[...ev.originalEvent.dataTransfer.files].forEach((file, i) => {
FilePond.find(document.getElementById("files_name")).addFile(file);
filesDropped = true;
});
}
if (filesDropped && !$('#uploadFilesModal').hasClass('show')){
$('#uploadFilesModal').modal('show');
}
});
{{end}}
$("#create_dir_form").submit(function (event) { $("#create_dir_form").submit(function (event) {
event.preventDefault(); event.preventDefault();
$('#createDirModal').modal('hide'); $('#createDirModal').modal('hide');
@ -712,7 +788,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
keepAlive(); keepAlive();
var keepAliveTimer = setInterval(keepAlive, 300000); var keepAliveTimer = setInterval(keepAlive, 300000);
var files = $("#files_name")[0].files; var files = FilePond.find(document.getElementById("files_name")).getFiles();
var has_errors = false; var has_errors = false;
var index = 0; var index = 0;
var success = 0; var success = 0;
@ -738,7 +814,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
var errorMessage = "Error uploading files"; var errorMessage = "Error uploading files";
let response; let response;
try { try {
var f = files[index]; var f = files[index].file;
var uploadPath = '{{.FileURL}}?path={{.CurrentDir}}'+encodeURIComponent("/"+f.name); var uploadPath = '{{.FileURL}}?path={{.CurrentDir}}'+encodeURIComponent("/"+f.name);
var lastModified; var lastModified;
try { try {
@ -887,7 +963,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
name: 'addFiles', name: 'addFiles',
titleAttr: "Upload files", titleAttr: "Upload files",
action: function (e, dt, node, config) { action: function (e, dt, node, config) {
document.getElementById("files_name").value = null; //FilePond.find(document.getElementById("files_name")).removeFiles();
$('#uploadFilesModal').modal('show'); $('#uploadFilesModal').modal('show');
}, },
enabled: true enabled: true

View file

@ -23,6 +23,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<link href="{{.StaticURL}}/vendor/datatables/fixedHeader.bootstrap4.min.css" rel="stylesheet"> <link href="{{.StaticURL}}/vendor/datatables/fixedHeader.bootstrap4.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet"> <link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.css" rel="stylesheet"> <link href="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/filepond/filepond.min.css" rel="stylesheet" />
<style> <style>
div.dataTables_wrapper span.selected-info, div.dataTables_wrapper span.selected-info,
div.dataTables_wrapper span.selected-item { div.dataTables_wrapper span.selected-item {
@ -45,7 +46,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div id="errorMsg" class="card mb-4 border-left-warning" style="display: none;"> <div id="errorMsg" class="card mb-4 border-left-warning" style="display: none;">
<div id="errorTxt" class="card-body text-form-error"></div> <div id="errorTxt" class="card-body text-form-error"></div>
</div> </div>
<div class="table-responsive"> <div id="tableContainer" class="table-responsive">
<table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0"> <table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0">
<thead> <thead>
<tr> <tr>
@ -76,7 +77,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
<form id="upload_files_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data"> <form id="upload_files_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data">
<div class="modal-body"> <div class="modal-body">
<input type="file" class="form-control-file" id="files_name" name="filenames" required multiple> <div id="uploadErrorMsg" class="card mb-4 border-left-warning" style="display: none;">
<div id="uploadErrorTxt" class="card-body text-form-error"></div>
</div>
<input type="file" id="files_name" name="filenames" required multiple>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button> <button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
@ -102,6 +106,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script> <script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script> <script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.min.js"></script> <script src="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.min.js"></script>
<script src="{{.StaticURL}}/vendor/filepond/filepond.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var spinnerDone = false; var spinnerDone = false;
@ -191,6 +196,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
return "far fa-file-code"; return "far fa-file-code";
case "zip": case "zip":
case "zipx": case "zipx":
case "7z":
case "rar": case "rar":
case "tar": case "tar":
case "gz": case "gz":
@ -223,6 +229,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
return meta.split('_').slice(1).join('_'); return meta.split('_').slice(1).join('_');
} }
const isDirectoryEntry = item => isEntry(item) && (getAsEntry(item) || {}).isDirectory;
const isEntry = item => 'webkitGetAsEntry' in item;
const getAsEntry = item => item.webkitGetAsEntry();
$(document).ready(function () { $(document).ready(function () {
$('#spinnerModal').on('shown.bs.modal', function () { $('#spinnerModal').on('shown.bs.modal', function () {
if (spinnerDone){ if (spinnerDone){
@ -230,9 +240,74 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
} }
}); });
{{if gt .Scope 1}}
FilePond.create(document.getElementById("files_name"),{
allowMultiple: true,
name: 'filenames',
maxFiles: 30,
credits: false,
required: true,
onwarning: function(error){
if (error.code == 0){
$('#uploadErrorTxt').text('You can upload a maximum of 30 files');
$('#uploadErrorMsg').show();
setTimeout(function () {
$('#uploadErrorMsg').hide();
}, 10000);
}
},
beforeAddFile: (fileItem) => new Promise(resolve => {
let num = 0;
FilePond.find(document.getElementById("files_name")).getFiles().forEach(function(val){
if (val.filename == fileItem.filename){
num++;
}
});
resolve(num == 1);
})
});
$('#tableContainer').on("dragover", function(ev){
ev.preventDefault();
$('#tableContainer').css('opacity','0.5');
});
$('#tableContainer').on("dragend dragleave", function(ev){
ev.preventDefault();
$('#tableContainer').css('opacity','1');
});
$('#tableContainer').on("drop", function(ev){
ev.preventDefault();
$('#tableContainer').css('opacity','1');
let filesDropped = false;
if (ev.originalEvent.dataTransfer.items) {
[...ev.originalEvent.dataTransfer.items].forEach((item, i) => {
if (item.kind === 'file') {
// if this is a directory just open the upload dialog
if (!isDirectoryEntry(item)){
FilePond.find(document.getElementById("files_name")).addFile(item.getAsFile());
filesDropped = true;
}
}
});
} else {
[...ev.originalEvent.dataTransfer.files].forEach((file, i) => {
FilePond.find(document.getElementById("files_name")).addFile(file);
filesDropped = true;
});
}
if (filesDropped && !$('#uploadFilesModal').hasClass('show')){
$('#uploadFilesModal').modal('show');
}
});
{{end}}
$("#upload_files_form").submit(function (event){ $("#upload_files_form").submit(function (event){
event.preventDefault(); event.preventDefault();
var files = $("#files_name")[0].files; var files = FilePond.find(document.getElementById("files_name")).getFiles();
var has_errors = false; var has_errors = false;
var index = 0; var index = 0;
var success = 0; var success = 0;
@ -255,7 +330,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
var errorMessage = "Error uploading files"; var errorMessage = "Error uploading files";
let response; let response;
try { try {
var f = files[index]; var f = files[index].file;
var uploadPath = '{{.UploadBaseURL}}'+fixedEncodeURIComponent("/"+escapeHTML(f.name)); var uploadPath = '{{.UploadBaseURL}}'+fixedEncodeURIComponent("/"+escapeHTML(f.name));
var lastModified; var lastModified;
try { try {
@ -345,7 +420,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
name: 'addFiles', name: 'addFiles',
titleAttr: "Upload files", titleAttr: "Upload files",
action: function (e, dt, node, config) { action: function (e, dt, node, config) {
document.getElementById("files_name").value = null; //FilePond.find(document.getElementById("files_name")).removeFiles();
$('#uploadFilesModal').modal('show'); $('#uploadFilesModal').modal('show');
}, },
enabled: true enabled: true