From 6c103375414a9b4f4e64375d2bcc1a33ea042582 Mon Sep 17 00:00:00 2001 From: Tamara Steinwender <tamara.steinwender@tugraz.at> Date: Wed, 22 Jul 2020 15:15:14 +0200 Subject: [PATCH] Adding folder function, upload files to nextcloud handling --- .../src/dbp-nextcloud-file-picker.js | 189 ++++++++++++------ packages/file-handling/src/file-sink.js | 27 ++- packages/file-handling/src/file-source.js | 1 + .../src/i18n/de/translation.json | 11 +- .../src/i18n/en/translation.json | 11 +- 5 files changed, 164 insertions(+), 75 deletions(-) diff --git a/packages/file-handling/src/dbp-nextcloud-file-picker.js b/packages/file-handling/src/dbp-nextcloud-file-picker.js index 8b2ee1ae..146f7488 100644 --- a/packages/file-handling/src/dbp-nextcloud-file-picker.js +++ b/packages/file-handling/src/dbp-nextcloud-file-picker.js @@ -10,7 +10,7 @@ import {classMap} from 'lit-html/directives/class-map.js'; import {humanFileSize} from 'dbp-common/i18next'; import Tabulator from 'tabulator-tables'; import nextcloudFileURL from 'consts:nextcloudFileURL'; -import MicroModal from './micromodal.es' +import MicroModal from './micromodal.es'; /** * NextcloudFilePicker web component @@ -32,8 +32,9 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { this.allowedMimeTypes = '*/*'; this.directoriesOnly = null; this.maxSelectedItems = true; - + this.loading = false; this._onReceiveWindowMessage = this.onReceiveWindowMessage.bind(this); + } static get scopedElements() { @@ -58,6 +59,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { allowedMimeTypes: { type: String, attribute: 'allowed-mime-types' }, directoriesOnly: { type: Boolean, attribute: 'directories-only' }, maxSelectedItems: { type: Number, attribute: 'max-selected-items' }, + }; } @@ -94,12 +96,21 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { placeholder:i18n.t('nextcloud-file-picker.no-data'), resizableColumns:false, columns: [ + {title: "", field: "type", align:"center", headerSort:false, width:50, responsive:1, formatter: (cell, formatterParams, onRendered) => { const icon_tag = that.constructor.getScopedTagName("dbp-icon"); let icon = `<${icon_tag} name="empty-file"></${icon_tag}>`; return (cell.getValue() === "directory") ? `<${icon_tag} name="folder"></${icon_tag}>` : icon; }}, - {title: i18n.t('nextcloud-file-picker.filename'), responsive: 0, widthGrow:5, minWidth: 150, field: "basename", sorter: "alphanum"}, + {title: i18n.t('nextcloud-file-picker.filename'), responsive: 0, widthGrow:5, minWidth: 150, field: "basename", sorter: "alphanum", + formatter: (cell) => { + var data = cell.getRow().getData(); + if(data.edit) + { + cell.getElement().classList.add("fokus-edit"); + } + return cell.getValue(); + }}, {title: i18n.t('nextcloud-file-picker.size'), responsive: 4, widthGrow:1, minWidth: 50, field: "size", formatter: (cell, formatterParams, onRendered) => { return cell.getRow().getData().type === "directory" ? "" : humanFileSize(cell.getValue());}}, {title: i18n.t('nextcloud-file-picker.mime-type'), responsive: 2, widthGrow:1, field: "mime", formatter: (cell, formatterParams, onRendered) => { @@ -122,7 +133,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { const hours = ("0" + timestamp.getHours()).slice(-2); const minutes = ("0" + timestamp.getMinutes()).slice(-2); return date + "." + month + "." + year + " " + hours + ":" + minutes; - }}, + }} ], initialSort:[ {column:"basename", dir:"asc"}, @@ -133,7 +144,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { const data = row.getData(); if (this.directoriesOnly) { - console.log("directory selected", data); } else { @@ -142,7 +152,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { this.directoryClicked(e, data); break; case "file": - console.log("file selected", data); break; } } @@ -194,6 +203,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { openFilePicker() { if (this.webDavClient === null) { + this.loading = true; this.statusText = i18n.t('nextcloud-file-picker.auth-progress'); const authUrl = this.authUrl + "?target-origin=" + encodeURIComponent(window.location.href); this.loginWindow = window.open(authUrl, "Nextcloud Login", @@ -212,7 +222,9 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { console.log("data", data); if (data.type === "webapppassword") { - this.loginWindow.close(); + if(this.loginWindow !== null) { + this.loginWindow.close(); + } const apiUrl = this.webDavUrl + "/" + data.loginName; console.log("url: ", this.webDavUrl); @@ -237,7 +249,8 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { * @param path */ loadDirectory(path) { - this.statusText = i18n.t('nextcloud-file-picker.loadpath-nextcloud-file-picker'); + this.loading = true; + this.statusText = i18n.t('nextcloud-file-picker.loadpath-nextcloud-file-picker', {name: this.nextcloudName}); this.lastDirectoryPath = this.directoryPath; this.directoryPath = path; @@ -247,6 +260,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { .getDirectoryContents(path, {details: true}) .then(contents => { console.log("contents", contents); + this.loading = false; this.statusText = ""; this.tabulatorTable.setData(contents.data); this.isPickerActive = true; @@ -258,6 +272,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { this.loadDirectory("/"); } else { + this.loading = false; this.statusText = error.message; this.isPickerActive = false; } @@ -267,6 +282,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { let reloadButton = html`<button class="button" title="${i18n.t('nextcloud-file-picker.refresh-nextcloud-file-picker')}" @click="${async () => { this.openFilePicker(); } }"><dbp-icon name="reload"></button>`; + this.loading = false; this.statusText = reloadButton; }); @@ -283,6 +299,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { } downloadFile(fileData) { + this.loading = true; this.statusText = "Loading " + fileData.filename + "..."; // https://github.com/perry-mitchell/webdav-client#getfilecontents @@ -298,42 +315,81 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { const event = new CustomEvent("dbp-nextcloud-file-picker-file-downloaded", { "detail": data, bubbles: true, composed: true }); this.dispatchEvent(event); - + this.loading = false; this.statusText = ""; }).catch(error => { console.error(error.message); + this.loading = false; this.statusText = error.message; }); } - uploadFiles(files) { - files.forEach((fileData) => this.uploadFile(fileData)); + sendDirectory(directory) { + this.loading = true; + this.statusText = "Uploading to " + directory[0].filename + "..."; + + const event = new CustomEvent("dbp-nextcloud-file-picker-file-uploaded", + { "detail": directory[0].filename, bubbles: true, composed: true }); + this.dispatchEvent(event); } - uploadFile(fileData) { - this.statusText = "Uploading " + fileData.filename + "..."; - console.log(fileData); + async uploadFiles(files, directory) { + console.log("before all file finished"); + let ret = false; + let ret_outer = true; + const start = async () => { + for (let index = 0; index < files.length; index++) { + ret = await this.uploadFile(files[index], directory); + if(ret === false) { + ret_outer = false; + break; + } + } + } - // https://github.com/perry-mitchell/webdav-client#putfilecontents - this.webDavClient - .putFileContents(fileData.filename, f) - .then(contents => { - // create file to send via event - const file = new File([contents], fileData.basename, { type: fileData.mime }); - console.log("binaryFile", file); + await start(); + //let ret = await files.forEach((file) => this.uploadFile(file, directory)); + console.log("all files finished", ret_outer); + return ret_outer; + } - // send event - const data = {"file": file, "data": fileData}; - const event = new CustomEvent("dbp-nextcloud-file-picker-file-uploaded", - { "detail": data, bubbles: true, composed: true }); - this.dispatchEvent(event); + async uploadFile(file, directory) { + console.log("before one file finished"); + let path = directory + "/" + file.name; + // https://github.com/perry-mitchell/webdav-client#putfilecontents + let ret = false; + try{ + let contents = await this.webDavClient + .putFileContents(path, file, { onUploadProgress: progress => { + console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`); + }}); + this.loading = false; + this.statusText = ""; + console.log("try finished"); + ret = true; + } catch(error ) { + console.error(error.message); + this.loading = false; + this.statusText = error.message; + } + console.log("after one file finished"); + return ret; + } - this.statusText = ""; - }).catch(error => { - console.error(error.message); - this.statusText = error.message; - }); + /** + * Add new folder with webdav + * + * + */ + addFolder() { + if(this._('#new-folder').value !== "") { + let folderPath = this.directoryPath + "/" +this._('#new-folder').value; + this.webDavClient.createDirectory(folderPath).then( contents => { this.loadDirectory(this.directoryPath); }).catch(error => { + this.loading = false; + this.statusText = i18n.t('nextcloud-file-picker.webdav-error'); + }); + } } /** @@ -575,7 +631,10 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { .nextcloud-footer-grid{ width: 100%; - display: grid; + display: flex; + align-items: center; + flex-direction: row-reverse; + justify-content: space-between; } .tabulator .tabulator-tableHolder{ @@ -588,16 +647,14 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { color: inherit; } + .add-folder{ + padding-top: 10px; + } + @media only screen and (orientation: portrait) and (max-device-width: 765px) { - - - - .nextcloud-nav{ - display: block; - } .nextcloud-nav h2 > a{ font-size: 1.3rem; @@ -611,11 +668,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { display: none; } - .select-button{ - display: block; - margin: auto; - } - .tabulator .tabulator-tableHolder{ white-space: inherit; } @@ -625,9 +677,12 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { .wrapper{ display: grid; - grid-template-areas: "header-l header-r" "content content"; + /*grid-template-areas: "header-l header-r" "content content"; grid-template-rows: 50px auto; - grid-template-columns: 50% 50%; + grid-template-columns: 50% 50%;*/ + grid-template-rows: auto 50px; + grid-template-columns: 100%; + grid-template-areas: "content" "footer"; } .nextcloud-header{ @@ -640,16 +695,18 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { } .nextcloud-intro{ - grid-column-start: header-l-start; + /*grid-column-start: header-l-start; grid-column-end: header-r-end; grid-row-start: header-l-start; - grid-row-end: content-end; + grid-row-end: content-end;*/ + grid-area: content; text-align: center; } .nextcloud-footer{ - grid-area: header-r; + /*grid-area: header-r;*/ padding-top: 0px; + grid-area: footer; } .info-box{ @@ -659,12 +716,18 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { .nextcloud-footer-grid{ display: flex; justify-content: end; + + justify-content: center; } .select-button{ margin: 0px; } + #new-folder{ + width: 86%; + } + } `; @@ -677,17 +740,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { return html` <div class="wrapper"> <link rel="stylesheet" href="${tabulatorCss}"> - <div class="nextcloud-header"> - <div class="button-wrapper ${classMap({hidden: !this.isPickerActive})}"> - <button class="button ${classMap({hidden: !this.isPickerActive})}" - title="${i18n.t('nextcloud-file-picker.folder-up')}" - @click="${() => { this.loadDirectory(this.getParentDirectoryPath()); }}"><dbp-icon name="arrow-left"></dbp-icon></button> - <button class="button ${classMap({hidden: !this.isPickerActive})}" - title="${i18n.t('nextcloud-file-picker.refresh-nextcloud-file-picker')}" - @click="${() => { this.loadDirectory(this.directoryPath); }}"><dbp-icon name="reload"></dbp-icon></button> - </div> - - </div> <div class="nextcloud-intro"> <div class="nextcloud-logo ${classMap({"nextcloud-logo-sm": this.isPickerActive})}"> ${this.getCloudLogo()} @@ -715,19 +767,26 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { <div class="nextcloud-content ${classMap({hidden: !this.isPickerActive})}"> <div class="nextcloud-nav"> <h2>${this.getBreadcrumb()}</h2> - <a class="int-link-external" - title="${i18n.t('nextcloud-file-picker.open-in-nextcloud', {name: this.nextcloudName})}" - href="${this.getNextCloudLink()}" target="_blank">${i18n.t('nextcloud-file-picker.open-in-nextcloud', {name: this.nextcloudName})} <svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="2.6842mm" width="2.6873mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 20.151879 20.141083"><g transform="translate(-258.5 -425.15)"><path style="stroke-linejoin:round;stroke:#000;stroke-linecap:round;stroke-width:1.2;fill:none" d="m266.7 429.59h-7.5029v15.002h15.002v-7.4634"/><path style="stroke-linejoin:round;stroke:#000;stroke-linecap:round;stroke-width:1.2;fill:none" d="m262.94 440.86 15.002-15.002"/><path style="stroke-linejoin:round;stroke:#000;stroke-linecap:round;stroke-width:1.2;fill:none" d="m270.44 425.86h7.499v7.499"/></g></svg></a> + <div class="add-folder ${classMap({hidden: !this.directoriesOnly})}"> + <input type="text" placeholder="${i18n.t('nextcloud-file-picker.new-folder-placeholder')}" name="new-folder" class="input" id="new-folder"> + <button class="button" + title="${i18n.t('nextcloud-file-picker.add-folder')}" + @click="${() => { this.addFolder(); }}"> + <dbp-icon name="plus" class="nextcloud-add-folder"></dbp-icon> + </button> + </div> </div> <table id="directory-content-table" class="force-no-select"></table> </div> <div class="nextcloud-footer ${classMap({hidden: !this.isPickerActive})}"> <div class="nextcloud-footer-grid"> - <button class="button select-button is-primary" - @click="${() => { this.downloadFiles(this.tabulatorTable.getSelectedData()); }}">${this.directoriesOnly ? (i18n.t('nextcloud-file-picker.select-folder')) : (i18n.t('nextcloud-file-picker.select-files'))}</button> + <button class="button select-button is-primary ${classMap({hidden: !this.directoriesOnly})}" + @click="${() => { this.sendDirectory(this.tabulatorTable.getSelectedData()); }}">${i18n.t('nextcloud-file-picker.select-folder')}</button> + <button class="button select-button is-primary ${classMap({hidden: this.directoriesOnly})}" + @click="${() => { this.downloadFiles(this.tabulatorTable.getSelectedData()); }}">${i18n.t('nextcloud-file-picker.select-files')}</button> <div class="block info-box ${classMap({hidden: this.statusText === ""})}"> - <dbp-mini-spinner style="font-size: 0.7em"></dbp-mini-spinner> + <dbp-mini-spinner style="font-size: 0.7em" class="${classMap({hidden: this.loading === false})}"></dbp-mini-spinner> ${this.statusText} </div> diff --git a/packages/file-handling/src/file-sink.js b/packages/file-handling/src/file-sink.js index 5737b532..e7c4a214 100644 --- a/packages/file-handling/src/file-sink.js +++ b/packages/file-handling/src/file-sink.js @@ -10,6 +10,7 @@ import {classMap} from 'lit-html/directives/class-map.js'; import FileSaver from 'file-saver'; import MicroModal from "./micromodal.es"; import * as fileHandlingStyles from './styles'; +import { send } from 'dbp-common/notification'; /** @@ -111,8 +112,25 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { return this.enabledDestinations.split(',').includes(source); } - uploadToNextcloud(directory) { - console.log(directory); + async uploadToNextcloud(directory) { + + let that = this; + const element = that._('#nextcloud-file-picker'); + console.log("davor"); + const finished = await element.uploadFiles(that.files, directory); + console.log("fertig", finished); + if(finished) { + MicroModal.close(); + console.log("close"); + send({ + "summary": i18n.t('file-sink.upload-success-title'), + "body": i18n.t('file-sink.upload-success-body', {name: this.nextcloudName}), + "type": "success", + "timeout": 5, + }); + } + + } preventDefaults (e) { @@ -157,6 +175,7 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { render() { return html` + <vpu-notification lang="de" client-id="my-client-id"></vpu-notification> <div class="modal micromodal-slide" id="modal-picker" aria-hidden="true"> <div class="modal-overlay" tabindex="-1" data-micromodal-close> <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-picker-title"> @@ -206,8 +225,8 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { auth-url="${this.nextcloudAuthUrl}" web-dav-url="${this.nextcloudWebDavUrl}" nextcloud-name="${this.nextcloudName}" - @dbp-nextcloud-file-picker-file-downloaded="${(event) => { - this.uploadToNextcloud(event.detail.file); + @dbp-nextcloud-file-picker-file-uploaded="${(event) => { + this.uploadToNextcloud(event.detail); }}"></dbp-nextcloud-file-picker> </div> </main> diff --git a/packages/file-handling/src/file-source.js b/packages/file-handling/src/file-source.js index 51fbe79f..72880778 100644 --- a/packages/file-handling/src/file-source.js +++ b/packages/file-handling/src/file-source.js @@ -358,6 +358,7 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { border-color: var(--FUBorderColorHighlight, purple); } + @media only screen and (orientation: portrait) and (max-device-width: 800px) { diff --git a/packages/file-handling/src/i18n/de/translation.json b/packages/file-handling/src/i18n/de/translation.json index 43fc3461..936fd36f 100644 --- a/packages/file-handling/src/i18n/de/translation.json +++ b/packages/file-handling/src/i18n/de/translation.json @@ -19,7 +19,9 @@ "local-intro": "{{amount}} Datei(en) als ZIP-Datei herunterladen", "local-button": "ZIP-Datei herunterladen", "modal-close": "Dialog schließen", - "nav-local": "Lokaler Computer" + "nav-local": "Lokaler Computer", + "upload-success-title": "Erfolgreich hochgeladen", + "upload-success-body": "Sie haben Ihre Dateien erfolgreich in {{name}} hochgeladen." }, "nextcloud-file-picker": { "open": "Nextcloud", @@ -29,7 +31,7 @@ "folder-home": "In das Home Verzeichnis springen", "select-files": "Dateien auswählen", "refresh-nextcloud-file-picker": "Erneut verbinden", - "loadpath-nextcloud-file-picker": "Das Nextcloud Verzeichnis wird geladen", + "loadpath-nextcloud-file-picker": "Das {{name}} Verzeichnis wird geladen", "load-path-link": "Gehe zu {{path}}", "auth-progress": "Anmeldung läuft", "last-modified": "Geändert", @@ -42,6 +44,9 @@ "connect-nextcloud": "{{name}} verbinden", "open-in-nextcloud": "In {{name}} öffnen", "no-data": "In diesem Ordner befinden sich keine Daten vom benötigten Typ.", - "select-folder": "In diesem Ordner hochladen" + "select-folder": "In diesem Ordner hochladen", + "webdav-error": "Etwas ist schief gelaufen", + "add-folder": "Neuen Ordner erstellen", + "new-folder-placeholder": "Neuer Ordner" } } diff --git a/packages/file-handling/src/i18n/en/translation.json b/packages/file-handling/src/i18n/en/translation.json index d200221b..07746e84 100644 --- a/packages/file-handling/src/i18n/en/translation.json +++ b/packages/file-handling/src/i18n/en/translation.json @@ -19,7 +19,9 @@ "local-intro": "Download {{amount}} file(s) as ZIP-file", "local-button": "Download ZIP-file", "modal-close": "Close dialog", - "nav-local": "My device" + "nav-local": "My device", + "upload-success-title": "Successful uploaded", + "upload-success-body": "You have successfully uploaded your files to {{name}}." }, "nextcloud-file-picker": { "open": "Nextcloud", @@ -29,7 +31,7 @@ "folder-up": "Jump to the home directory", "select-files": "Select files", "refresh-nextcloud-file-picker": "Connect again", - "loadpath-nextcloud-file-picker": "Loading directory from Nextcloud", + "loadpath-nextcloud-file-picker": "Loading directory from {{name}}", "load-path-link": "Go to {{path}}", "auth-progress": "Authentification in progress", "last-modified": "Last modified", @@ -42,6 +44,9 @@ "connect-nextcloud": "Connect {{name}}", "open-in-nextcloud": "Open in {{name}}", "no-data": "No data avaible in this folder.", - "select-folder": "Upload in selected folder" + "select-folder": "Upload in selected folder", + "webdav-error": "Something went wrong", + "add-folder": "Add new folder", + "new-folder-placeholder": "New folder" } } -- GitLab