Browse Source

WebClient: improve file uploads

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
Nicola Murino 1 year ago
parent
commit
fc023748c1

+ 7 - 0
internal/httpd/api_shares.go

@@ -357,6 +357,13 @@ func (s *httpdServer) uploadFileToShare(w http.ResponseWriter, r *http.Request)
 		return
 	}
 	defer common.Connections.Remove(connection.GetID())
+
+	if getBoolQueryParam(r, "mkdir_parents") {
+		if err = connection.CheckParentDirs(path.Dir(filePath)); err != nil {
+			sendAPIResponse(w, r, err, "Error checking parent directories", getMappedStatusCode(err))
+			return
+		}
+	}
 	if err := doUploadFile(w, r, connection, filePath); err != nil {
 		dataprovider.UpdateShareLastUse(&share, -1) //nolint:errcheck
 	}

+ 1 - 1
static/locales/en/translation.json

@@ -360,7 +360,7 @@
             "err_403": "$t(fs.upload.err_generic). $t(fs.err_403)",
             "err_429": "$t(fs.upload.err_generic). $t(fs.err_429)",
             "err_dir_overwrite": "$t(fs.upload.err_generic). There are directories with the same name as the files: {{- val}}",
-            "overwrite_text": "File conflict detected. Do you want to overwrite the following files?"
+            "overwrite_text": "Conflict detected. Do you want to overwrite the following files/directories?"
         },
         "quota_usage": {
             "title": "Quota usage",

+ 1 - 1
static/locales/it/translation.json

@@ -360,7 +360,7 @@
             "err_403": "$t(fs.upload.err_generic). $t(fs.err_403)",
             "err_429": "$t(fs.upload.err_generic). $t(fs.err_429)",
             "err_dir_overwrite": "$t(fs.upload.err_generic). Ci sono cartelle con lo stesso nome dei file: {{- val}}",
-            "overwrite_text": "Rilevato conflitto di file. Vuoi sovrascrivere i seguenti file?"
+            "overwrite_text": "Rilevato conflitto. Vuoi sovrascrivere i seguenti file/cartelle?"
         },
         "quota_usage": {
             "title": "Utilizzo quota",

+ 7 - 0
templates/common/base.html

@@ -341,6 +341,13 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
     .shortcut {
         font-family: monospace; color: #666;
     }
+
+    .overflow-auto {
+        overflow: auto;
+    }
+    .visibility-auto {
+        content-visibility: auto;
+    }
 </style>
 {{- end}}
 

+ 1 - 0
templates/webadmin/admins.html

@@ -227,6 +227,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/connections.html

@@ -127,6 +127,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/defender.html

@@ -132,6 +132,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/eventactions.html

@@ -132,6 +132,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/eventrules.html

@@ -183,6 +183,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/folders.html

@@ -164,6 +164,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/groups.html

@@ -150,6 +150,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/iplists.html

@@ -263,6 +263,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: handleResponseData,
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/roles.html

@@ -151,6 +151,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 1 - 0
templates/webadmin/users.html

@@ -279,6 +279,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 69 - 19
templates/webclient/files.html

@@ -159,7 +159,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         <div id="upload_files_empty_container" class="d-none mt-10">
             <form id="upload_files_empty_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data">
                 <div class="fv-row">
-                    <div class="dropzone" id="upload_files_empty">
+                    <div class="dropzone mh-350px overflow-auto visibility-auto" id="upload_files_empty">
                         <div class="dz-message needsclick align-items-center">
                             <i class="ki-duotone ki-file-up fs-3x text-primary">
                                 <span class="path1"></span>
@@ -235,6 +235,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
     //{{- end}}
     //{{- end}}
 
+    const dzPreviewTemplate = `<div class="d-flex align-items-center mb-2">
+                                 <span class="bullet bullet-dot bg-primary me-2"></span>
+                                 <div class="text-break text-wrap text-left"><span class="fs-5 fw-semibold" data-dz-name></span>&nbsp;(<span class="fs-5 fw-semibold" data-custom-size></span>)</div>
+                               </div>
+                               <div class="dz-error-message d-none" data-dz-errormessage></div>
+                               <div class="dz-progress d-none"><span class="dz-upload" data-dz-uploadprogress></span></div>
+                              `;
+
     //{{- if not .ShareUploadBaseURL}}
     const supportedEditExtensions = ["csv", "bat", "dyalog", "apl", "asc", "pgp", "sig", "asn", "asn1", "b", "bf",
             "c", "h", "ino", "cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx", "cob", "cpy", "cbl", "cs", "clj",
@@ -556,6 +564,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;
@@ -1979,6 +1988,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         let has_errors = false;
         let index = 0;
         let success = 0;
+        let checkedDirs = [];
         $('#errorMsg').addClass("d-none");
         $('#loading_message').text("");
         KTApp.showPageLoading();
@@ -1996,10 +2006,20 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
 
             let f = files[index];
             let uploadPath;
+            let name = f.name;
+            let mkdirParents = "false";
+            if (f.fullPath){
+                name = f.fullPath;
+                let dirName = name.substr(0, name.lastIndexOf("/"));
+                if (!checkedDirs.includes(dirName)){
+                    mkdirParents = "true";
+                    checkedDirs.push(dirName);
+                }
+            }
             //{{- if .ShareUploadBaseURL}}
-            uploadPath = '{{.ShareUploadBaseURL}}' + encodeURIComponent("/" + f.name);
+            uploadPath = '{{.ShareUploadBaseURL}}' + encodeURIComponent("/" + name)+"?mkdir_parents="+mkdirParents;
             //{{- else}}
-            uploadPath = '{{.FileURL}}?path={{.CurrentDir}}' + encodeURIComponent("/" + f.name);
+            uploadPath = '{{.FileURL}}?path={{.CurrentDir}}' + encodeURIComponent("/" + name)+"&mkdir_parents="+mkdirParents;
             //{{- end}}
             let lastModified;
             try {
@@ -2064,9 +2084,27 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
             });
         }
 
+        let filesArray = [];
+        let dirsArray = [];
+        if (files.length > 0){
+            for (let i = 0; i < files.length; i++){
+                if (files[i].fullPath){
+                    let dirName = files[i].fullPath.split('/')[0];
+                    if (!dirsArray.includes(dirName)){
+                        dirsArray.push(dirName);
+                    }
+                    if (!filesArray.includes(dirName)){
+                        filesArray.push(dirName);
+                    }
+                } else {
+                    filesArray.push(files[i].name);
+                }
+            }
+        }
+
         CheckExist.fire({
             operation: "upload",
-            files: files,
+            files: filesArray,
             path: "{{.CurrentDir}}"
         }).then((result)=> {
             if (result.error) {
@@ -2079,7 +2117,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
             let existingFiles = [];
             let existingDirs = [];
             $.each(result.data, function (key, item) {
-                if (item.type === "1") {
+                if (item.type === "1" && !dirsArray.includes(item.name)) {
                     existingDirs.push(item.name);
                 } else {
                     existingFiles.push(item.name);
@@ -2122,15 +2160,9 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
             var promiseResolve;
 
             function doCheck(operation, files, target) {
-                let filesArray = [];
-                if (files && files.length > 0){
-                    for (let i = 0; i < files.length; i++){
-                        filesArray.push(files[i].name);
-                    }
-                }
                 let path = '{{.CheckExistURL}}?op='+encodeURIComponent(operation)+"&path="+target;
                 axios.post(path, {
-                    files: filesArray
+                    files: files
                 }, {
                     headers: {
                         timeout: 15000,
@@ -2176,12 +2208,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         var dropzone =  new Dropzone("#upload_files", {
             url: "{{.FilesURL}}?path={{.CurrentDir}}",
             paramName: "filenames",
-            maxFiles: 250,
+            createImageThumbnails: false,
+            maxFiles: null,
             maxFilesize: null,
             autoQueue: false,
-            addRemoveLinks: true,
+            addRemoveLinks: false,
             autoProcessQueue: false,
             filesizeBase: 1000,
+            previewTemplate: dzPreviewTemplate,
             init: function() {
                 var dropzone = this;
                 $("#upload_files_button").click(function(){
@@ -2191,18 +2225,27 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         });
 
         dropzone.on("addedfile", file => {
-            file.previewElement.querySelector(".dz-progress").style.display = 'none';
+            for (node of file.previewElement.querySelectorAll("[data-custom-size]")) {
+                node.textContent = fileSizeIEC(file.size);
+            }
+            if (file.fullPath){
+                for (var node of file.previewElement.querySelectorAll("[data-dz-name]")) {
+                    node.textContent = file.fullPath;
+                }
+            }
         });
 
         var dropzoneEmpty =  new Dropzone("#upload_files_empty", {
             url: "{{.FilesURL}}?path={{.CurrentDir}}",
             paramName: "filenames",
-            maxFiles: 250,
+            createImageThumbnails: false,
+            maxFiles: null,
             maxFilesize: null,
             autoQueue: false,
-            addRemoveLinks: true,
+            addRemoveLinks: false,
             autoProcessQueue: false,
             filesizeBase: 1000,
+            previewTemplate: dzPreviewTemplate,
             init: function() {
                 var dropzoneEmpty = this;
                 $("#upload_files_empty_button").click(function(){
@@ -2212,7 +2255,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         });
 
         dropzoneEmpty.on("addedfile", file => {
-            file.previewElement.querySelector(".dz-progress").style.display = 'none';
+            for (node of file.previewElement.querySelectorAll("[data-custom-size]")) {
+                node.textContent = fileSizeIEC(file.size);
+            }
+            if (file.fullPath){
+                for (var node of file.previewElement.querySelectorAll("[data-dz-name]")) {
+                    node.textContent = file.fullPath;
+                }
+            }
         });
 
         $('#modal_video_player').on('hide.bs.modal', function () {
@@ -2406,7 +2456,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
             <div class="modal-body">
                 <form id="upload_files_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data">
                     <div class="fv-row">
-                        <div class="dropzone" id="upload_files">
+                        <div class="dropzone mh-350px overflow-auto visibility-auto" id="upload_files">
                             <div class="dz-message needsclick align-items-center">
                                 <i class="ki-duotone ki-file-up fs-3x text-primary"><span class="path1"></span><span class="path2"></span></i>
                                 <div class="ms-4">

+ 1 - 0
templates/webclient/shares.html

@@ -265,6 +265,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
                     dataSrc: "",
                     error: function ($xhr, textStatus, errorThrown) {
                         $(".dataTables_processing").hide();
+                        $('#loader').addClass("d-none");
                         let txt = "";
                         if ($xhr) {
                             let json = $xhr.responseJSON;

+ 31 - 5
templates/webclient/shareupload.html

@@ -33,7 +33,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
             {{- template "errmsg" ""}}
             <form id="upload_files_form" action="{{.UploadBasePath}}" method="POST" enctype="multipart/form-data">
                 <div class="fv-row">
-                    <div class="dropzone" id="upload_files">
+                    <div class="dropzone mh-350px overflow-auto visibility-auto" id="upload_files">
                         <div class="dz-message needsclick align-items-center">
                             <i class="ki-duotone ki-file-up fs-3x text-primary"><span class="path1"></span><span class="path2"></span></i>
                             <div class="ms-4">
@@ -58,6 +58,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         let has_errors = false;
         let index = 0;
         let success = 0;
+        let checkedDirs = [];
         $('#errorMsg').addClass("d-none");
         $('#loading_message').text("");
         KTApp.showPageLoading();
@@ -83,7 +84,17 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
             }
 
             let f = files[index];
-            let uploadPath = '{{.UploadBasePath}}/'+encodeURIComponent(f.name);
+            let name = f.name;
+            let mkdirParents = "false";
+            if (f.fullPath){
+                name = f.fullPath;
+                let dirName = name.substr(0, name.lastIndexOf("/"));
+                if (!checkedDirs.includes(dirName)){
+                    mkdirParents = "true";
+                    checkedDirs.push(dirName);
+                }
+            }
+            let uploadPath = '{{.UploadBasePath}}/'+encodeURIComponent(name)+"?mkdir_parents="+mkdirParents;
             let lastModified;
             try {
                 lastModified = f.lastModified;
@@ -154,12 +165,20 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         var dropzone =  new Dropzone("#upload_files", {
             url: "{{.UploadBasePath}}",
             paramName: "filenames",
-            maxFiles: 200,
+            createImageThumbnails: false,
+            maxFiles: null,
             maxFilesize: null,
             autoQueue: false,
-            addRemoveLinks: true,
+            addRemoveLinks: false,
             autoProcessQueue: false,
             filesizeBase: 1000,
+            previewTemplate: `<div class="d-flex align-items-center mb-2">
+                                 <span class="bullet bullet-dot bg-primary me-2"></span>
+                                 <div class="text-break text-wrap text-left"><span class="fs-5 fw-semibold" data-dz-name></span>&nbsp;(<span class="fs-5 fw-semibold" data-custom-size></span>)</div>
+                               </div>
+                               <div class="dz-error-message d-none" data-dz-errormessage></div>
+                               <div class="dz-progress d-none"><span class="dz-upload" data-dz-uploadprogress></span></div>
+                              `,
             init: function() {
                 var dropzone = this;
                 $("#upload_files_button").click(function(){
@@ -169,7 +188,14 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
         });
 
         dropzone.on("addedfile", file => {
-            file.previewElement.querySelector(".dz-progress").style.display = 'none';
+            for (node of file.previewElement.querySelectorAll("[data-custom-size]")) {
+                node.textContent = fileSizeIEC(file.size);
+            }
+            if (file.fullPath){
+                for (var node of file.previewElement.querySelectorAll("[data-dz-name]")) {
+                    node.textContent = file.fullPath;
+                }
+            }
         });
     });