Skip to content
Snippets Groups Projects
Commit a6ddb2b6 authored by Bekerle, Patrizio's avatar Bekerle, Patrizio :fire:
Browse files

Merge branch 'filehandling-overwrite-dialog' into 'master'

Filehandling overwrite dialog

See merge request !1
parents 5cd090f5 83f31cc6
No related branches found
No related tags found
1 merge request!1Filehandling overwrite dialog
Pipeline #12669 passed
...@@ -36,6 +36,17 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -36,6 +36,17 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
this._onReceiveWindowMessage = this.onReceiveWindowMessage.bind(this); this._onReceiveWindowMessage = this.onReceiveWindowMessage.bind(this);
this.folderIsSelected = i18n.t('nextcloud-file-picker.load-in-folder'); this.folderIsSelected = i18n.t('nextcloud-file-picker.load-in-folder');
this.generatedFilename = '';
this.replaceFilename = '';
this.customFilename = '';
this.uploadFileObject = null;
this.uploadFileDirectory = null;
this.fileList = [];
this.fileNameCounter = 1;
this.activeDirectoryRights = 'SGDNVCK';
this.activeDirectoryACL = '';
this.forAll = false;
this.uploadCount = 0;
} }
static get scopedElements() { static get scopedElements() {
...@@ -61,6 +72,12 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -61,6 +72,12 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
allowedMimeTypes: { type: String, attribute: 'allowed-mime-types' }, allowedMimeTypes: { type: String, attribute: 'allowed-mime-types' },
directoriesOnly: { type: Boolean, attribute: 'directories-only' }, directoriesOnly: { type: Boolean, attribute: 'directories-only' },
maxSelectedItems: { type: Number, attribute: 'max-selected-items' }, maxSelectedItems: { type: Number, attribute: 'max-selected-items' },
loading: { type: Boolean, attribute: false },
replaceFilename: { type: String, attribute: false },
uploadFileObject: { type: Object, attribute: false },
uploadFileDirectory: { type: String, attribute: false },
activeDirectoryRights: { type: String, attribute: false },
activeDirectoryACL: { type: String, attribute: false },
}; };
} }
...@@ -134,7 +151,9 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -134,7 +151,9 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
const hours = ("0" + timestamp.getHours()).slice(-2); const hours = ("0" + timestamp.getHours()).slice(-2);
const minutes = ("0" + timestamp.getMinutes()).slice(-2); const minutes = ("0" + timestamp.getMinutes()).slice(-2);
return date + "." + month + "." + year + " " + hours + ":" + minutes; return date + "." + month + "." + year + " " + hours + ":" + minutes;
}} }},
{title: "rights", field: "props.permissions"},//, visible:false},
{title: "acl", field: "props.acl-list.acl.acl-permissions"}//, visible:false}
], ],
initialSort:[ initialSort:[
{column:"basename", dir:"asc"}, {column:"basename", dir:"asc"},
...@@ -153,6 +172,10 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -153,6 +172,10 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
const data = row.getData(); const data = row.getData();
if (this.directoriesOnly) { if (this.directoriesOnly) {
// comment out if you want to navigate through folders with double click
const data = row.getData();
this.directoryClicked(e, data);
this.folderIsSelected = i18n.t('nextcloud-file-picker.load-in-folder');
} }
else else
{ {
...@@ -174,10 +197,12 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -174,10 +197,12 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
} }
}); });
if (this.tabulatorTable.browserMobile === false)
// Strg + click select mode on desktop
/*if (this.tabulatorTable.browserMobile === false)
{ {
this.tabulatorTable.options.selectableRangeMode = "click"; this.tabulatorTable.options.selectableRangeMode = "click";
} }*/
function checkFileType(data, filterParams) { function checkFileType(data, filterParams) {
// check if file is allowed // check if file is allowed
if (typeof data.mime === 'undefined') { if (typeof data.mime === 'undefined') {
...@@ -199,7 +224,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -199,7 +224,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
} }
if (typeof this.directoriesOnly !== 'undefined' && this.directoriesOnly) if (typeof this.directoriesOnly !== 'undefined' && this.directoriesOnly)
{ {
console.log("filter " + this.directoriesOnly); //console.log("filter " + this.directoriesOnly);
this.tabulatorTable.setFilter([ this.tabulatorTable.setFilter([
{field:"type", type:"=", value:"directory"}, {field:"type", type:"=", value:"directory"},
]); ]);
...@@ -224,8 +249,11 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -224,8 +249,11 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
} }
onReceiveWindowMessage(event) { onReceiveWindowMessage(event) {
if (this.webDavClient === null)
{
const data = event.data; const data = event.data;
console.log("data", data); console.log("data", data);
console.log("context", this.directoriesOnly);
if (data.type === "webapppassword") { if (data.type === "webapppassword") {
if (this.loginWindow !== null) { if (this.loginWindow !== null) {
...@@ -233,7 +261,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -233,7 +261,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
} }
const apiUrl = this.webDavUrl + "/" + data.loginName; const apiUrl = this.webDavUrl + "/" + data.loginName;
console.log("url: ", this.webDavUrl); //console.log("url: ", this.webDavUrl);
// see https://github.com/perry-mitchell/webdav-client/blob/master/API.md#module_WebDAV.createClient // see https://github.com/perry-mitchell/webdav-client/blob/master/API.md#module_WebDAV.createClient
this.webDavClient = createClient( this.webDavClient = createClient(
...@@ -243,10 +271,10 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -243,10 +271,10 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
password: data.token password: data.token
} }
); );
this.loadDirectory(this.directoryPath); this.loadDirectory(this.directoryPath);
} }
} }
}
/** /**
* Loads the directory from WebDAV * Loads the directory from WebDAV
...@@ -260,48 +288,105 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -260,48 +288,105 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
this.directoryPath = path; this.directoryPath = path;
// see https://github.com/perry-mitchell/webdav-client#getdirectorycontents // see https://github.com/perry-mitchell/webdav-client#getdirectorycontents
if (this.webDavClient === null)
{
// client is broken reload try to reset & reconnect
this.tabulatorTable.clearData();
this.webDavClient = null;
let reloadButton = html`${i18n.t('nextcloud-file-picker.something-went-wrong')} <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;
}
this.webDavClient this.webDavClient
.getDirectoryContents(path, {details: true}) .getDirectoryContents(path, {details: true, data: "<?xml version=\"1.0\"?>" +
"<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\" xmlns:ocs=\"http://open-collaboration-services.org/ns\">" +
" <d:prop>" +
" <d:getlastmodified />" +
" <d:resourcetype />" +
" <d:getcontenttype />" +
" <d:getcontentlength />" +
" <d:getetag />" +
" <oc:permissions />" +
" <nc:acl-list>" +
" <nc:acl>" +
" <nc:acl-permissions />" +
" </nc:acl>" +
" </nc:acl-list>" +
" </d:prop>" +
"</d:propfind>"})
.then(contents => { .then(contents => {
console.log("contents", contents);
this.loading = false; this.loading = false;
this.statusText = ""; this.statusText = "";
this.tabulatorTable.setData(contents.data); this.tabulatorTable.setData(contents.data);
console.log("!!!!!!!!!!!!!!!!!!!!!!!!", contents.data)
this.isPickerActive = true; this.isPickerActive = true;
if (!this.activeDirectoryRights.includes("CK") && !this.activeDirectoryRights.includes("NV")) {
this._("#download-button").setAttribute("disabled", "true");
}
else {
this._("#download-button").removeAttribute("disabled");
}
}).catch(error => { }).catch(error => {
console.error(error.message); console.error(error.message);
// on Error: try to reload with home directory // on Error: try to reload with home directory
if (path != "/") { if (path != "/" && this.webDavClient !== null){
this.loadDirectory("/"); this.loadDirectory("/");
} }
else { else {
this.loading = false; this.loading = false;
this.statusText = error.message; this.statusText = error.message;
this.isPickerActive = false; this.isPickerActive = false;
} this.tabulatorTable.clearData();
// client is broken reload try to reset & reconnect
this.webDavClient = null; this.webDavClient = null;
let reloadButton = html`<button class="button" let reloadButton = html`${i18n.t('nextcloud-file-picker.something-went-wrong')} <button class="button"
title="${i18n.t('nextcloud-file-picker.refresh-nextcloud-file-picker')}" title="${i18n.t('nextcloud-file-picker.refresh-nextcloud-file-picker')}"
@click="${async () => { this.openFilePicker(); } }"><dbp-icon name="reload"></button>`; @click="${async () => { this.openFilePicker(); } }"><dbp-icon name="reload"></button>`;
this.loading = false; this.loading = false;
this.statusText = reloadButton; this.statusText = reloadButton;
}
}); });
} }
/**
* Event Triggered when a directory in tabulator table is clicked
*
* @param event
* @param file
*/
directoryClicked(event, file) { directoryClicked(event, file) {
// save rights of clicked directory
this.activeDirectoryRights = file.props.permissions;
if (typeof file.props['acl-list'] !== "undefined" &&
typeof file.props['acl-list']['acl']['acl-permissions'] !== "undefined" && file.props['acl-list']['acl']['acl-permissions']) {
this.activeDirectoryACL = file.props['acl-list']['acl']['acl-permissions'];
}
else {
this.activeDirectoryACL = '';
}
this.loadDirectory(file.filename); this.loadDirectory(file.filename);
event.preventDefault(); event.preventDefault();
} }
/**
* Download all files
*
* @param files
*/
downloadFiles(files) { downloadFiles(files) {
this.tabulatorTable.deselectRow();
files.forEach((fileData) => this.downloadFile(fileData)); files.forEach((fileData) => this.downloadFile(fileData));
MicroModal.close();
} }
/**
* Download a single file
*
* @param fileData
*/
downloadFile(fileData) { downloadFile(fileData) {
this.loading = true; this.loading = true;
this.statusText = "Loading " + fileData.filename + "..."; this.statusText = "Loading " + fileData.filename + "...";
...@@ -312,8 +397,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -312,8 +397,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
.then(contents => { .then(contents => {
// create file to send via event // create file to send via event
const file = new File([contents], fileData.basename, { type: fileData.mime }); const file = new File([contents], fileData.basename, { type: fileData.mime });
console.log("binaryFile", file);
// send event // send event
const data = {"file": file, "data": fileData}; const data = {"file": file, "data": fileData};
const event = new CustomEvent("dbp-nextcloud-file-picker-file-downloaded", const event = new CustomEvent("dbp-nextcloud-file-picker-file-downloaded",
...@@ -328,7 +411,13 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -328,7 +411,13 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
}); });
} }
/**
* Send the directory to filesink
*
* @param directory
*/
sendDirectory(directory) { sendDirectory(directory) {
this.tabulatorTable.deselectRow();
let path; let path;
if (!directory[0]) if (!directory[0])
...@@ -339,61 +428,352 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -339,61 +428,352 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
path = directory[0].filename; path = directory[0].filename;
} }
this.loading = true; this.loading = true;
this.statusText = "Uploading to " + path + " ..."; this.statusText = i18n.t('nextcloud-file-picker.upload-to', {path: path});
const event = new CustomEvent("dbp-nextcloud-file-picker-file-uploaded", const event = new CustomEvent("dbp-nextcloud-file-picker-file-uploaded",
{ "detail": path, bubbles: true, composed: true }); { "detail": path, bubbles: true, composed: true });
this.dispatchEvent(event); this.dispatchEvent(event);
} }
/**
* Upload Files to a directory
*
* @param files
* @param directory
*/
uploadFiles(files, directory) {
this.loading = true;
this.statusText = i18n.t('nextcloud-file-picker.upload-to', {path: directory});
this.fileList = files;
this.forAll = false;
this.uploadFile(directory);
}
async uploadFiles(files, directory) { /**
console.log("before all file finished"); * Upload a single file from this.filelist to given directory
let ret = false; *
let ret_outer = true; * @param directory
const start = async () => { */
for (let index = 0; index < files.length; index++) { async uploadFile(directory) {
ret = await this.uploadFile(files[index], directory); if (this.fileList.length !== 0) {
if (ret === false) { let file = this.fileList[0];
ret_outer = false; this.replaceFilename = file.name;
break; let path = directory + "/" + file.name;
// https://github.com/perry-mitchell/webdav-client#putfilecontents
let that = this;
this.loading = true;
this.statusText = i18n.t('nextcloud-file-picker.upload-to', {path: path});
let contents = await this.webDavClient
.putFileContents(path, file, { overwrite: false, onUploadProgress: progress => {
console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`);
}}).then(function() {
that.uploadCount += 1;
that.fileList.shift();
that.uploadFile(directory);
}).catch(error => {
if (error.message.search("412") !== -1 || error.message.search("403") !== -1) {
this.generatedFilename = this.getNextFilename();
this._("#replace-filename").value = this.generatedFilename;
if (this.forAll) {
this.uploadFileObject = file;
this.uploadFileDirectory = directory;
this.uploadFileAfterConflict();
}
else {
this.replaceModalDialog(file, directory);
} }
} }
});
} }
else {
this.loadDirectory(this.directoryPath);
this.loading = false;
this.statusText = "";
this._("#replace_mode_all").checked = false;
this.forAll = false;
this.customFilename = '';
const event = new CustomEvent("dbp-nextcloud-file-picker-file-uploaded-finished",
{ bubbles: true, composed: true , detail: this.uploadCount});
this.uploadCount = 0;
this.dispatchEvent(event);
await start(); }
//let ret = await files.forEach((file) => this.uploadFile(file, directory));
console.log("all files finished", ret_outer);
return ret_outer;
} }
async uploadFile(file, directory) { /**
console.log("before one file finished"); * Upload a file after a conflict happens on webdav side
let path = directory + "/" + file.name; *
*/
async uploadFileAfterConflict() {
let path = "";
let overwrite = false;
let file = this.uploadFileObject;
let directory = this.uploadFileDirectory;
if (this._("input[name='replacement']:checked").value === "ignore") {
MicroModal.close(this._("#replace-modal"));
this.forAll ? this.fileList = [] : this.fileList.shift();
this.uploadFile(directory);
return true;
} else if (this._("input[name='replacement']:checked").value === "new-name") {
if (this.generatedFilename !== this._("#replace-filename").value) {
this.customFilename = this._("#replace-filename").value;
}
path = directory + "/" + this._("#replace-filename").value;
this.replaceFilename = this._("#replace-filename").value;
} else {
path = directory + "/" + this.uploadFileObject.name;
overwrite = true;
}
this.loading = true;
this.statusText = i18n.t('nextcloud-file-picker.upload-to', {path: path});
let that = this;
// https://github.com/perry-mitchell/webdav-client#putfilecontents // https://github.com/perry-mitchell/webdav-client#putfilecontents
let ret = false;
try{
let contents = await this.webDavClient let contents = await this.webDavClient
.putFileContents(path, file, { overwrite: false, onUploadProgress: progress => { .putFileContents(path, file, {
overwrite: overwrite, onUploadProgress: progress => {
console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`); console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`);
}}); }
}).then(content => {
MicroModal.close(this._("#replace-modal"));
this.uploadCount += 1;
that.fileList.shift();
that.uploadFile(directory);
}).catch(error => {
if (error.message.search("412") !== -1) {
MicroModal.close(that._("#replace-modal"));
this.generatedFilename = this.getNextFilename();
this._("#replace-filename").value = this.generatedFilename;
if (this.forAll) {
this.uploadFileObject = file;
this.uploadFileDirectory = directory;
this.uploadFileAfterConflict();
}
else {
this.replaceModalDialog(file, directory);
}
}
});
this.fileNameCounter = 1;
}
/**
* Check permissions of a given file in the active directory
* no rename: if you dont have create permissions
* no replace: if you dont have write permissions
*
* R = Share, S = Shared Folder, M = Group folder or external source, G = Read, D = Delete, NV / NVW = Write, CK = Create
*
* @param file
* @return number
*/
checkRights(file) {
// nextcloud permissions
let file_perm = 0;
let active_directory_perm = this.activeDirectoryRights;
let rows = this.tabulatorTable.searchRows("basename", "=", this.replaceFilename);
if (typeof rows[0] !== 'undefined' && rows[0]) {
file_perm = rows[0].getData().props.permissions;
}
else {
file_perm = "";
}
/* ACL permissions: If ACL > permssions comment this in
if (this.activeDirectoryACL !== '')
{
console.log("ACL SET");
active_directory_perm = "MG";
if (this.activeDirectoryACL & (1 << (3 - 1)))
{
active_directory_perm = "CK";
console.log("ACL CREATE");
}
if (this.activeDirectoryACL & (1 << (2 - 1)))
{
active_directory_perm += "NV";
console.log("ACL WRITE");
}
}
// if file has acl rights take that
if (typeof rows[0].getData().props['acl-list'] !== 'undefined' && rows[0].getData().props['acl-list'] &&
rows[0].getData().props['acl-list']['acl']['acl-permissions'] !== '')
{
console.log("FILE HAS ACL");
file_perm = "MG";
if (rows[0].getData().props['acl-list']['acl']['acl-permissions'] & (1 << (3 - 1)))
{
file_perm = "CK";
console.log("FILE ACL CREATE");
}
if (rows[0].getData().props['acl-list']['acl']['acl-permissions'] & (1 << (2 - 1)))
{
file_perm += "NV";
console.log("FILE ACL WRITE");
}
}
*/
// all allowed
if (active_directory_perm.includes("CK") && file_perm.includes("NV"))
{
return -1;
}
// read only file but you can write to directory = only create and no edit
if (active_directory_perm.includes("CK") && !file_perm.includes("NV"))
{
return 1;
}
// only edit and no create
if (!active_directory_perm.includes("CK") && file_perm.includes("NV"))
{
return 2;
}
// read only directory and read only file
return 0;
}
/**
* Open the replace Modal Dialog with gui where forbidden actions are disabled
*
* @param file, directory
*/
replaceModalDialog(file, directory) {
this.uploadFileObject = file;
this.uploadFileDirectory = directory;
let rights = this.checkRights(file);
// read only directory or read only file
if (rights === 0) {
this.loading = false;
this.statusText = i18n.t('nextcloud-file-picker.readonly');
return;
}
// read only file but you can write to directory = only create and no edit
else if (rights === 1) {
this.loading = false; this.loading = false;
this.statusText = i18n.t('nextcloud-file-picker.onlycreate');
this._("#replace-replace").setAttribute("disabled", "true");
this._("#replace-new-name").removeAttribute("disabled");
this._("#replace-replace").checked = false;
this._("#replace-new-name").checked = true;
this.setInputFieldVisibility();
this._("#replace-new-name").focus();
}
// only edit and no create
else if (rights === 2) {
this.loading = false;
this.statusText = i18n.t('nextcloud-file-picker.onlyedit');
this._("#replace-new-name").setAttribute("disabled", "true");
this._("#replace-replace").removeAttribute("disabled");
this._("#replace-new-name").checked = false;
this._("#replace-replace").checked = true;
this.setInputFieldVisibility();
this._("#replace-replace").focus();
}
// all allowed
else {
this._("#replace-new-name").removeAttribute("disabled");
this._("#replace-replace").removeAttribute("disabled");
this._("#replace-replace").checked = false;
this._("#replace-new-name").checked = true;
this.setInputFieldVisibility();
this._("#replace-new-name").focus();
}
MicroModal.show(this._('#replace-modal'), {
onClose: modal => {
this.statusText = "";
this.loading = false;},
});
}
/**
* Returns a filename with the next counter number.
*
* @returns {string} The next filename
*/
getNextFilename() {
let nextFilename = "";
let splitFilename;
if (this.forAll && this.customFilename !== '') {
splitFilename = this.customFilename.split(".");
}
else {
splitFilename = this.replaceFilename.split(".");
}
let splitBracket = splitFilename[0].split('(');
if (splitBracket.length > 1) {
let numberString = splitBracket[1].split(')');
if (numberString.length > 1 && !isNaN(parseInt(numberString[0]))) {
let number = parseInt(numberString[0]);
this.fileNameCounter = number + 1;
nextFilename = splitBracket[0] + "(" + this.fileNameCounter + ")";
}
else {
nextFilename = splitFilename[0] + "(" + this.fileNameCounter + ")";
}
}
else {
nextFilename = splitFilename[0] + "(" + this.fileNameCounter + ")";
}
if (splitFilename.length > 1) {
for(let i = 1; i < splitFilename.length; i++) {
nextFilename = nextFilename + "." + splitFilename[i];
}
}
this.fileNameCounter++;
return nextFilename;
}
/**
* Disables or enables the input field for the new file name
*/
setInputFieldVisibility() {
this._("#replace-filename").disabled = !this._("#replace-new-name").checked;
}
/**
* Returns text for the cancel button depending on number of files
*
* @returns {string} correct cancel text
*/
getCancelText() {
if (this.fileList.length > 1) {
return i18n.t('nextcloud-file-picker.replace-cancel-all');
}
return i18n.t('nextcloud-file-picker.replace-cancel');
}
/**
*
*/
cancelOverwrite() {
this.statusText = ""; this.statusText = "";
console.log("try finished");
ret = true;
} catch(error ) {
console.error(error.message);
this.loading = false; this.loading = false;
this.statusText = error.message; this.fileList = [];
} }
console.log("after one file finished");
return ret; /**
*
*/
setRepeatForAllConflicts() {
this.forAll = this._("#replace_mode_all").checked;
} }
/** /**
* Add new folder with webdav * Add new folder with webdav
* *
*
*/ */
addFolder() { addFolder() {
if (this._('#new-folder').value !== "") { if (this._('#new-folder').value !== "") {
...@@ -424,7 +804,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -424,7 +804,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
*/ */
getBreadcrumb() { getBreadcrumb() {
let htmlpath = []; let htmlpath = [];
htmlpath[0] = html`<a @click="${() => { this.loadDirectory("/"); }}" title="${i18n.t('nextcloud-file-picker.folder-home')}"><dbp-icon name="home"></dbp-icon> </a>`; htmlpath[0] = html`<span class="breadcrumb"><a @click="${() => { this.loadDirectory("/"); }}" title="${i18n.t('nextcloud-file-picker.folder-home')}"><dbp-icon name="home"></dbp-icon> </a></span>`;
const directories = this.directoryPath.split('/'); const directories = this.directoryPath.split('/');
if (directories[1] === "") if (directories[1] === "")
{ {
...@@ -439,7 +819,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -439,7 +819,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
path += directories[j]; path += directories[j];
} }
htmlpath[i] = html`<a @click="${() => { this.loadDirectory(path); }}" title="${i18n.t('nextcloud-file-picker.load-path-link', {path: directories[i]})}">${directories[i]}</a>`; htmlpath[i] = html`<span class="muted"> › </span><span class="breadcrumb"><a @click="${() => { this.loadDirectory(path); }}" title="${i18n.t('nextcloud-file-picker.load-path-link', {path: directories[i]})}">${directories[i]}</a></span>`;
} }
return htmlpath; return htmlpath;
...@@ -484,6 +864,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -484,6 +864,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
${commonStyles.getGeneralCSS()} ${commonStyles.getGeneralCSS()}
${commonStyles.getButtonCSS()} ${commonStyles.getButtonCSS()}
${commonStyles.getTextUtilities()} ${commonStyles.getTextUtilities()}
${commonStyles.getModalDialogCSS()}
.block { .block {
margin-bottom: 10px; margin-bottom: 10px;
...@@ -581,14 +962,14 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -581,14 +962,14 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
background-color: white; background-color: white;
} }
.tabulator-row.tabulator-selected:hover, .tabulator-row.tabulator-selected{ .tabulator-row.tabulator-selectable.tabulator-selectable:hover{
background-color: var(--dbp-dark); background-color: var(--dbp-secondary-bg-color);
color: var(--dbp-light); color: var(--dbp-secondary-text-color);
} }
.tabulator-row.tabulator-selectable:hover{ .tabulator-row.tabulator-selectable.tabulator-selected:hover, .tabulator-row.tabulator-selected{
background-color: #eee; background-color: var(--dbp-dark);
color: var(--dbp-dark); color: var(--dbp-light);
} }
.tabulator .tabulator-header .tabulator-col .tabulator-col-content{ .tabulator .tabulator-header .tabulator-col .tabulator-col-content{
...@@ -664,6 +1045,80 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -664,6 +1045,80 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
padding-top: 10px; padding-top: 10px;
} }
#replace-modal-box {
display: flex;
flex-direction: column;
justify-content: center;
padding: 30px;
max-height: 450px;
min-height: 450px;
min-width: 380px;
max-width: 190px;
}
#replace-modal-box .modal-header {
display: flex;
justify-content: space-evenly;
align-items: baseline;
}
#replace-modal-box .modal-header h2 {
font-size: 1.2rem;
padding-right: 5px;
}
#replace-modal-box .modal-content {
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-evenly;
}
#replace-modal-box .radio-btn {
margin-right: 5px;
}
#replace-modal-box .modal-content label {
display: block;
width: 100%;
}
#replace-modal-box #replace-filename {
display: block;
width: 100%;
margin-top: 8px;
}
#replace-modal-box input[type="text"]:disabled {
color: #aaa;
}
#replace-modal-box .modal-content div {
display: flex;
}
#replace-modal-box .modal-footer {
padding-top: 15px;
}
#replace-modal-box .modal-footer .modal-footer-btn {
display: flex;
justify-content: space-between;
padding-bottom: 15px;
}
.breadcrumb, .muted{
color: var(--dbp-muted-text);
}
.breadcrumb:last-child{
color: inherit;
}
input:disabled+label{
color: #aaa;
}
@media only screen @media only screen
and (orientation: portrait) and (orientation: portrait)
and (max-device-width: 765px) { and (max-device-width: 765px) {
...@@ -728,8 +1183,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -728,8 +1183,6 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
.nextcloud-footer-grid{ .nextcloud-footer-grid{
display: flex; display: flex;
justify-content: end;
justify-content: center; justify-content: center;
} }
...@@ -740,6 +1193,12 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -740,6 +1193,12 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
#new-folder{ #new-folder{
width: 86%; width: 86%;
} }
#replace-modal-box {
min-width: 100%;
max-width: 100%;
}
} }
`; `;
} }
...@@ -792,7 +1251,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -792,7 +1251,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
<div class="nextcloud-footer ${classMap({hidden: !this.isPickerActive})}"> <div class="nextcloud-footer ${classMap({hidden: !this.isPickerActive})}">
<div class="nextcloud-footer-grid"> <div class="nextcloud-footer-grid">
<button class="button select-button is-primary ${classMap({hidden: !this.directoriesOnly})}" <button id="download-button" class="button select-button is-primary ${classMap({hidden: !this.directoriesOnly})}"
@click="${() => { this.sendDirectory(this.tabulatorTable.getSelectedData()); }}">${this.folderIsSelected}</button> @click="${() => { this.sendDirectory(this.tabulatorTable.getSelectedData()); }}">${this.folderIsSelected}</button>
<button class="button select-button is-primary ${classMap({hidden: this.directoriesOnly})}" <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> @click="${() => { this.downloadFiles(this.tabulatorTable.getSelectedData()); }}">${i18n.t('nextcloud-file-picker.select-files')}</button>
...@@ -806,6 +1265,53 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) { ...@@ -806,6 +1265,53 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
</div> </div>
</div> </div>
</div> </div>
<div class="modal micromodal-slide" id="replace-modal" aria-hidden="true">
<div class="modal-overlay" tabindex="-2" data-micromodal-close>
<div class="modal-container" id="replace-modal-box" role="dialog" aria-modal="true" aria-labelledby="replace-modal-title" >
<header class="modal-header">
<h2 id="replace-modal-title">
${i18n.t('nextcloud-file-picker.replace-title-1')}
<span style="word-break: break-all;">${this.replaceFilename}</span>
${i18n.t('nextcloud-file-picker.replace-title-2')}.
</h2>
<button title="${i18n.t('file-sink.modal-close')}" class="modal-close" aria-label="Close modal" data-micromodal-close>
<dbp-icon title="${i18n.t('file-sink.modal-close')}" name="close" class="close-icon"></dbp-icon>
</button>
</header>
<main class="modal-content" id="replace-modal-content">
<h3>
${i18n.t('nextcloud-file-picker.replace-text')}?
</h3>
<div>
<input type="radio" id="replace-new-name" class="radio-btn" name="replacement" value="new-name" checked @click="${() => {this.setInputFieldVisibility();}}">
<label for="new-name">${i18n.t('nextcloud-file-picker.replace-new_name')}:
<input type="text" id="replace-filename" name="replace-filename" value="" onClick="this.select();">
</label>
</div>
<div>
<input type="radio" id="replace-replace" class="radio-btn" name="replacement" value="replace" @click="${() => {this.setInputFieldVisibility();}}">
<label for="replace">${i18n.t('nextcloud-file-picker.replace-replace')}</label>
</div>
<div>
<input type="radio" class="radio-btn" name="replacement" value="ignore" @click="${() => {this.setInputFieldVisibility();}}">
<label for="ignore">${i18n.t('nextcloud-file-picker.replace-skip')}</label>
</div>
</main>
<footer class="modal-footer">
<div class="modal-footer-btn">
<button class="button" data-micromodal-close aria-label="Close this dialog window" @click="${() => {this.cancelOverwrite();}}">${this.getCancelText()}</button>
<button class="button select-button is-primary" @click="${() => {this.uploadFileAfterConflict();}}">OK</button>
</div>
<div>
<input type="checkbox" id="replace_mode_all" name="replace_mode_all" value="replace_mode_all" @click="${() => {this.setRepeatForAllConflicts();}}>
<label for="replace_mode_all">${i18n.t('nextcloud-file-picker.replace-mode-all')}</label>
</div>
</footer>
</div>
</div>
</div>
`; `;
} }
} }
...@@ -67,7 +67,14 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -67,7 +67,14 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
let zip = new JSZip(); let zip = new JSZip();
let fileNames = []; let fileNames = [];
// add all signed pdf-files // download one file not compressed!
if (this.files.length === 1)
{
FileSaver.saveAs(this.files[0], this.files[0].filename);
return;
}
// download all files compressed
this.files.forEach((file) => { this.files.forEach((file) => {
let fileName = file.name; let fileName = file.name;
...@@ -114,24 +121,23 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -114,24 +121,23 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
} }
async uploadToNextcloud(directory) { async uploadToNextcloud(directory) {
let that = this; let that = this;
const element = that._('#nextcloud-file-picker'); const element = that._('#nextcloud-file-picker');
console.log("davor"); console.log("davor");
const finished = await element.uploadFiles(that.files, directory); await element.uploadFiles(that.files, directory);
console.log("fertig", finished); }
if(finished) {
MicroModal.close(); finishedFileUpload(event) {
console.log("close"); MicroModal.close(this._('#modal-picker'));
if (event.detail > 0)
{
send({ send({
"summary": i18n.t('file-sink.upload-success-title'), "summary": i18n.t('file-sink.upload-success-title'),
"body": i18n.t('file-sink.upload-success-body', {name: this.nextcloudName}), "body": i18n.t('file-sink.upload-success-body', {name: this.nextcloudName, count: event.detail}),
"type": "success", "type": "success",
"timeout": 5, "timeout": 5,
}); });
} }
} }
preventDefaults (e) { preventDefaults (e) {
...@@ -142,13 +148,14 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -142,13 +148,14 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
openDialog() { openDialog() {
console.log("openDialog"); console.log("openDialog");
MicroModal.show(this._('#modal-picker'), { MicroModal.show(this._('#modal-picker'), {
onClose: modal => { this.isDialogOpen = false; } onClose: modal => { this.isDialogOpen = false; },
closeTrigger: 'data-custom-close',
}); });
} }
closeDialog() { closeDialog() {
console.log("closeDialog"); console.log("closeDialog");
MicroModal.close(); MicroModal.close(this._('#modal-picker'));
} }
static get styles() { static get styles() {
...@@ -179,7 +186,7 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -179,7 +186,7 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
return html` return html`
<vpu-notification lang="de" client-id="my-client-id"></vpu-notification> <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 micromodal-slide" id="modal-picker" aria-hidden="true">
<div class="modal-overlay" tabindex="-1" data-micromodal-close> <div class="modal-overlay" tabindex="-1" data-custom-close>
<div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-picker-title"> <div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-picker-title">
<nav class="modal-nav"> <nav class="modal-nav">
<div title="${i18n.t('file-sink.nav-local')}" <div title="${i18n.t('file-sink.nav-local')}"
...@@ -196,7 +203,7 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -196,7 +203,7 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
</div> </div>
</nav> </nav>
<div class="modal-header"> <div class="modal-header">
<button title="${i18n.t('file-sink.modal-close')}" class="modal-close" aria-label="Close modal" data-micromodal-close> <button title="${i18n.t('file-sink.modal-close')}" class="modal-close" aria-label="Close modal" data-custom-close @click="${() => { this.closeDialog()}}">
<dbp-icon title="${i18n.t('file-sink.modal-close')}" name="close" class="close-icon"></dbp-icon> <dbp-icon title="${i18n.t('file-sink.modal-close')}" name="close" class="close-icon"></dbp-icon>
</button> </button>
<p class="modal-context"> ${this.context}</p> <p class="modal-context"> ${this.context}</p>
...@@ -207,12 +214,12 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -207,12 +214,12 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
<div class="source-main ${classMap({"hidden": this.activeDestination !== "local"})}"> <div class="source-main ${classMap({"hidden": this.activeDestination !== "local"})}">
<div id="zip-download-block"> <div id="zip-download-block">
<div class="block"> <div class="block">
${this.text || i18n.t('file-sink.local-intro', {'amount': this.files.length})} ${this.text || i18n.t('file-sink.local-intro', {'count': this.files.length})}
</div> </div>
<button class="button is-primary" <button class="button is-primary"
?disabled="${this.disabled}" ?disabled="${this.disabled}"
@click="${() => { this.downloadCompressedFiles(); }}"> @click="${() => { this.downloadCompressedFiles(); }}">
${this.buttonLabel || i18n.t('file-sink.local-button')} ${this.buttonLabel || i18n.t('file-sink.local-button', {'count': this.files.length})}
</button> </button>
</div> </div>
</div> </div>
...@@ -229,6 +236,9 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -229,6 +236,9 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
nextcloud-name="${this.nextcloudName}" nextcloud-name="${this.nextcloudName}"
@dbp-nextcloud-file-picker-file-uploaded="${(event) => { @dbp-nextcloud-file-picker-file-uploaded="${(event) => {
this.uploadToNextcloud(event.detail); this.uploadToNextcloud(event.detail);
}}"
@dbp-nextcloud-file-picker-file-uploaded-finished="${(event) => {
this.finishedFileUpload(event);
}}"></dbp-nextcloud-file-picker> }}"></dbp-nextcloud-file-picker>
</div> </div>
</main> </main>
......
...@@ -205,6 +205,8 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -205,6 +205,8 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
* @param file * @param file
*/ */
sendFileEvent(file) { sendFileEvent(file) {
MicroModal.close(this._('#modal-picker'));
console.log("close filesource modal.")
const data = {"file": file}; const data = {"file": file};
const event = new CustomEvent("dbp-file-source-file-selected", { "detail": data, bubbles: true, composed: true }); const event = new CustomEvent("dbp-file-source-file-selected", { "detail": data, bubbles: true, composed: true });
this.dispatchEvent(event); this.dispatchEvent(event);
...@@ -319,7 +321,7 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -319,7 +321,7 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
closeDialog() { closeDialog() {
console.log("closeDialog"); console.log("closeDialog");
MicroModal.close(); MicroModal.close(this._('#modal-picker'));
} }
static get styles() { static get styles() {
......
...@@ -16,12 +16,15 @@ ...@@ -16,12 +16,15 @@
"nav-local": "Lokaler Computer" "nav-local": "Lokaler Computer"
}, },
"file-sink": { "file-sink": {
"local-intro": "{{amount}} Datei(en) als ZIP-Datei herunterladen", "local-intro": "{{count}} Datei herunterladen",
"local-button": "ZIP-Datei herunterladen", "local-intro_plural": "{{count}} Dateien als ZIP-Datei herunterladen",
"local-button": "Datei herunterladen",
"local-button_plural": "ZIP-Datei herunterladen",
"modal-close": "Dialog schließen", "modal-close": "Dialog schließen",
"nav-local": "Lokaler Computer", "nav-local": "Lokaler Computer",
"upload-success-title": "Erfolgreich hochgeladen", "upload-success-title": "Erfolgreich hochgeladen",
"upload-success-body": "Sie haben Ihre Dateien erfolgreich in {{name}} hochgeladen." "upload-success-body": "Sie haben {{count}} Datei erfolgreich in {{name}} hochgeladen.",
"upload-success-body_plural": "Sie haben {{count}} Dateien erfolgreich in {{name}} hochgeladen."
}, },
"nextcloud-file-picker": { "nextcloud-file-picker": {
"open": "Nextcloud", "open": "Nextcloud",
...@@ -49,6 +52,20 @@ ...@@ -49,6 +52,20 @@
"add-folder": "Neuen Ordner erstellen", "add-folder": "Neuen Ordner erstellen",
"new-folder-placeholder": "Neuer Ordner", "new-folder-placeholder": "Neuer Ordner",
"load-in-folder": "Ins aktuelle Verzeichnis laden", "load-in-folder": "Ins aktuelle Verzeichnis laden",
"load-to-folder": "Im ausgewählten Ordner hochladen" "load-to-folder": "Im ausgewählten Ordner hochladen",
"replace-title-1": "Es ist bereits eine Datei mit dem Namen ",
"replace-title-2": " vorhanden",
"replace-text": "Was möchten Sie tun",
"replace-new_name": "Neuer Name",
"replace-replace": "Ersetzen",
"replace-skip": "Überspringen",
"replace-cancel": "Abbrechen",
"replace-cancel-all": "Alle abbrechen",
"replace-mode-all": "Für alle zukünftigen Konflikte übernehmen",
"something-went-wrong": "Etwas ist schief gelaufen. Bitte verbinden Sie sich erneut",
"upload-to": "Es wird nach {{- path}} hochgeladen ...",
"readonly": "Sie dürfen in diesem Ordner nichts hochladen.",
"onlycreate": "Sie dürfen in diesem Ordner nur neue Dateien erstellen.",
"onlyedit": "Sie dürfen in diesem Ordner nur Dateien bearbeiten."
} }
} }
...@@ -16,19 +16,22 @@ ...@@ -16,19 +16,22 @@
"nav-local": "My device" "nav-local": "My device"
}, },
"file-sink": { "file-sink": {
"local-intro": "Download {{amount}} file(s) as ZIP-file", "local-intro": "Download {{count}} file",
"local-button": "Download ZIP-file", "local-intro_plural": "Download {{count}} files als ZIP",
"local-button": "Download file",
"local-button_plural": "Download ZIP-file",
"modal-close": "Close dialog", "modal-close": "Close dialog",
"nav-local": "My device", "nav-local": "My device",
"upload-success-title": "Successful uploaded", "upload-success-title": "Successful uploaded",
"upload-success-body": "You have successfully uploaded your files to {{name}}." "upload-success-body": "You have successfully uploaded {{count}} file to {{name}}.",
"upload-success-body_plural": "You have successfully uploaded {{count}} files to {{name}}."
}, },
"nextcloud-file-picker": { "nextcloud-file-picker": {
"open": "Nextcloud", "open": "Nextcloud",
"open-nextcloud-file-picker": "Select files from your {{name}}", "open-nextcloud-file-picker": "Select files from your {{name}}",
"folder-last": "Jump to the last directory", "folder-last": "Jump to the last directory",
"folder-up": "Jump to the parent directory", "folder-up": "Jump to the parent directory",
"folder-up": "Jump to the home directory", "folder-home": "Jump to the home directory",
"select-files": "Select files", "select-files": "Select files",
"refresh-nextcloud-file-picker": "Connect again", "refresh-nextcloud-file-picker": "Connect again",
"loadpath-nextcloud-file-picker": "Loading directory from {{name}}", "loadpath-nextcloud-file-picker": "Loading directory from {{name}}",
...@@ -49,6 +52,21 @@ ...@@ -49,6 +52,21 @@
"add-folder": "Add new folder", "add-folder": "Add new folder",
"new-folder-placeholder": "New folder", "new-folder-placeholder": "New folder",
"load-in-folder": "Load into the current directory", "load-in-folder": "Load into the current directory",
"load-to-folder": "Upload to the selected folder" "load-to-folder": "Upload to the selected folder",
"replace-title-1": "There is already a file with the name ",
"replace-title-2": " in this location",
"replace-text": "What do you want to do",
"replace-new_name": "New Name",
"replace-replace": "Replace",
"replace-skip": "Ignore",
"replace-cancel": "Cancel",
"replace-cancel-all": "Cancel all",
"replace-mode-all": "Do this for the next conflicts",
"something-went-wrong": "Something went wrong. Please reload",
"upload-to": "Uploading to {{path}} ...",
"readonly": "You are not allowed to uploade files in this directory.",
"onlycreate": "You are only allowed to create new files in this directory.",
"onlyedit": "You are only allowed to edit files in this directory."
} }
} }
...@@ -185,7 +185,13 @@ var MicroModal = function () { ...@@ -185,7 +185,13 @@ var MicroModal = function () {
}, { }, {
key: "closeModalById", key: "closeModalById",
value: function closeModalById(targetModal) { value: function closeModalById(targetModal) {
this.modal = document.getElementById(targetModal); // added support to pass on an element or an id -> for webcomponents
if (targetModal instanceof HTMLElement) {
this.modal = targetModal;
}
else {
this.model = document.getElementById(targetModal);
}
if (this.modal) this.closeModal(); if (this.modal) this.closeModal();
} }
}, { }, {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment