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