diff --git a/.github/config/.finnishwords.txt b/.github/config/.finnishwords.txt index 170fd6641..a28e3eb9d 100644 --- a/.github/config/.finnishwords.txt +++ b/.github/config/.finnishwords.txt @@ -3,6 +3,7 @@ ajaksi alapuolisesta alaviiva alikansio +alikansioiden alikansion alikansiot alkanut @@ -313,6 +314,7 @@ ne niiden niin nimellä +nimet nimetä nimeä nimi @@ -390,6 +392,7 @@ pienemmissä pienennä piilota piste +pitkiä poiketa pois poissa @@ -644,6 +647,7 @@ uusia vaatii vaihda vaihto +vain valikkoon valikossa valinnan @@ -674,6 +678,7 @@ virhe virheellinen voi voida +voidaan voidaksesi voit voivat diff --git a/CHANGELOG.md b/CHANGELOG.md index 4504f7102..df7c1abae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Move upload, download fetch calls to workers to reduce messaging overhead - (GL #1145) Added new api for modifying write access from container's access control list - (GL #1151) New folder moved to the top of the table for a while when created via Upload modal or Create Folder modal +- (GL #1147) Add a toast when user cannot download archive due to ustar limits on file path ### Changed diff --git a/swift_browser_ui_frontend/src/common/globalFunctions.js b/swift_browser_ui_frontend/src/common/globalFunctions.js index b7926ee7c..296f7540f 100644 --- a/swift_browser_ui_frontend/src/common/globalFunctions.js +++ b/swift_browser_ui_frontend/src/common/globalFunctions.js @@ -242,3 +242,25 @@ export async function updateObjectsAndObjectTags( } } } + +export function checkIfCanDownloadTar(objs, isSubfolder) { + if (!objs) { + return true; + } + else if (isSubfolder && objs.length === 1) { + //no tar when single file in a subfolder + return true; + } + else { + //file or subfolder name max 99 chars, prefix max 154 + const pathOk = objs.every( + (path) => { + const elements = path.split("/"); + if (elements.find(el => el.length > 99)) return false; + if (elements.length > 2 && + path.length - elements.slice(-1)[0].length > 154) return false; + return true; + }); + return pathOk; + } +} diff --git a/swift_browser_ui_frontend/src/common/lang.js b/swift_browser_ui_frontend/src/common/lang.js index fcbbe37b8..72b50ea5a 100644 --- a/swift_browser_ui_frontend/src/common/lang.js +++ b/swift_browser_ui_frontend/src/common/lang.js @@ -185,6 +185,9 @@ let default_translations = { invalid_share_ids: " are not valid Share IDs. Please remove them.", }, download: " Download", + downloadFiles: "Files can only be downloaded " + + "individually because there are file or subfolder names longer than " + + "99 characters.", largeDownMessage: "No large (> 1GiB) downloads enabled. Click to " + "enable them for the duration of the session.", @@ -578,6 +581,9 @@ let default_translations = { " eivät ole kelvollisia jakamistunnuksia. Poistakaa ne.", }, download: " Lataa", + downloadFiles: "Tiedostot voidaan ladata vain " + + "erikseen, koska tiedostojen tai alikansioiden nimet ovat " + + "yli 99 merkkiä pitkiä.", largeDownMessage: "Suurten tiedostojen (> 1Gt) lataus täytyy hyväksyä " + "erikseen. Paina hyväksyäksesi suuret lataukset " + diff --git a/swift_browser_ui_frontend/src/components/CObjectTable.vue b/swift_browser_ui_frontend/src/components/CObjectTable.vue index d4dad73ed..0e3c0a551 100644 --- a/swift_browser_ui_frontend/src/components/CObjectTable.vue +++ b/swift_browser_ui_frontend/src/components/CObjectTable.vue @@ -44,6 +44,7 @@ import { getPrefix, getPaginationOptions, checkIfItemIsLastOnPage, + checkIfCanDownloadTar, } from "@/common/globalFunctions"; import { setPrevActiveElement, @@ -413,14 +414,24 @@ export default { return obj.name.startsWith(object.name); }) .map(item => item.name); - if (DEV) console.log(subfolderFiles); - this.$store.state.socket.addDownload( - this.$route.params.container, - subfolderFiles, - this.$route.params.owner ? this.$route.params.owner : "", - ).then(() => { - if (DEV) console.log(`Started downloading subfolder ${object.name}`); - }); + const canDownload = checkIfCanDownloadTar(subfolderFiles, true); + if (canDownload) { + this.$store.state.socket.addDownload( + this.$route.params.container, + subfolderFiles, + this.$route.params.owner ? this.$route.params.owner : "", + ).then(() => { + if (DEV) console.log(`Started downloading subfolder ${object.name}`); + }); + } else { + document.querySelector("#container-error-toasts") + .addToast( + { progress: false, + type: "error", + duration: 6000, + message: this.$t("message.downloadFiles")}, + ); + } } else { this.$store.state.socket.addDownload( this.$route.params.container, diff --git a/swift_browser_ui_frontend/src/components/ContainerTable.vue b/swift_browser_ui_frontend/src/components/ContainerTable.vue index 7567d5835..072565460 100644 --- a/swift_browser_ui_frontend/src/components/ContainerTable.vue +++ b/swift_browser_ui_frontend/src/components/ContainerTable.vue @@ -44,6 +44,7 @@ import { getPaginationOptions, toggleCopyFolderModal, checkIfItemIsLastOnPage, + checkIfCanDownloadTar, } from "@/common/globalFunctions"; import { setPrevActiveElement, @@ -52,6 +53,7 @@ import { import { toRaw } from "vue"; import { swiftDeleteContainer, + getObjects, } from "@/common/api"; export default { @@ -277,15 +279,16 @@ export default { text: true, size: "small", title: this.$t("message.download"), - onClick: () => { - this.beginDownload( + onClick: async () => { + await this.containerDownload( item.name, item.owner ? item.owner : "", ); }, target: "_blank", path: mdiTrayArrowDown, - disabled: item.owner && item.accessRights?.length === 0, + disabled: (item.owner && item.accessRights?.length === 0) + || !item.bytes, }, }, }, @@ -362,8 +365,8 @@ export default { path: mdiDotsHorizontal, title: this.$t("message.options"), size: "small", - disabled: item.owner && - item.accessRights?.length === 0, + disabled: (item.owner && + item.accessRights?.length === 0) || !item.bytes, }, }, }, @@ -503,6 +506,24 @@ export default { if (DEV) console.log(`Started downloading all objects from container ${container}`); }); }, + async containerDownload(containerName, owner) { + let containerObjs = await getObjects(this.active.id, containerName); + containerObjs = containerObjs.map(obj => obj.name); + + const canDownload = checkIfCanDownloadTar( + containerObjs, false); + if (canDownload) { + this.beginDownload(containerName, owner); + } else { + document.querySelector("#container-error-toasts") + .addToast( + { progress: false, + type: "error", + duration: 6000, + message: this.$t("message.downloadFiles")}, + ); + } + }, getEmptyText() { if (this.$route.name == "SharedFrom") { return this.$t("message.emptyProject.sharedFrom");