Skip to content
Snippets Groups Projects
Commit 693ae15d authored by Steinwender, Tamara's avatar Steinwender, Tamara
Browse files

Outsourcing the file-handling clipboard part to an external webcomponent

parent 026eff9e
No related branches found
No related tags found
No related merge requests found
...@@ -5,15 +5,32 @@ import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element'; ...@@ -5,15 +5,32 @@ import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element';
import {Icon, MiniSpinner} from '@dbp-toolkit/common'; import {Icon, MiniSpinner} from '@dbp-toolkit/common';
import * as commonStyles from '@dbp-toolkit/common/styles'; import * as commonStyles from '@dbp-toolkit/common/styles';
import * as fileHandlingStyles from './styles'; import * as fileHandlingStyles from './styles';
import Tabulator from "tabulator-tables";
import {humanFileSize} from "@dbp-toolkit/common/i18next";
import {classMap} from 'lit-html/directives/class-map.js';
import * as commonUtils from "@dbp-toolkit/common/utils";
import {name as pkgName} from "../package.json";
import {send} from "@dbp-toolkit/common/notification";
/** /**
* NextcloudFilePicker web component * Clipboard web component
*/ */
export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) { export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) {
constructor() { constructor() {
super(); super();
this.lang = 'de'; this.lang = 'de';
this.authUrl = ''; this.authUrl = '';
this.allowedMimeTypes = '*/*';
this.clipboardSource = false;
this.clipboardFiles = {files: ''};
this.clipboardSelectBtnDisabled = true;
this.clipboardSelectBtnDisabled = true;
this.showSelectAllButton = true;
this.tabulatorTable = null;
this.maxSelectedItems = true;
this._onReceiveBeforeUnload = this.onReceiveBeforeUnload.bind(this);
} }
static get scopedElements() { static get scopedElements() {
...@@ -31,6 +48,12 @@ export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) { ...@@ -31,6 +48,12 @@ export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) {
...super.properties, ...super.properties,
lang: { type: String }, lang: { type: String },
authUrl: { type: String, attribute: 'auth-url' }, authUrl: { type: String, attribute: 'auth-url' },
allowedMimeTypes: { type: String, attribute: 'allowed-mime-types' },
showSelectAllButton: { type: Boolean, attribute: true },
clipboardSelectBtnDisabled: { type: Boolean, attribute: true },
clipboardFiles: {type: Object, attribute: 'clipboard-files'},
clipboardSource: {type: Boolean, attribute: 'clipboard-source'},
}; };
} }
...@@ -41,19 +64,291 @@ export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) { ...@@ -41,19 +64,291 @@ export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) {
case "lang": case "lang":
i18n.changeLanguage(this.lang); i18n.changeLanguage(this.lang);
break; break;
case "clipboardFiles":
this.generateClipboardTable();
break;
} }
console.log("source", this.clipboardSource);
}); });
super.update(changedProperties); super.update(changedProperties);
} }
disconnectedCallback() { disconnectedCallback() {
//We doesn't want to deregister this event, because we want to use this event over activities
//window.removeEventListener('beforeunload', this._onReceiveBeforeUnload);
super.disconnectedCallback();
} }
connectedCallback() { connectedCallback() {
super.connectedCallback();
const that = this;
this.updateComplete.then(() => {
// see: http://tabulator.info/docs/4.7
this.tabulatorTable = new Tabulator(this._("#clipboard-content-table"), {
layout: "fitColumns",
selectable: this.maxSelectedItems,
selectableRangeMode: "drag",
responsiveLayout: true,
placeholder: i18n.t('nextcloud-file-picker.no-data-type'),
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" class="nextcloud-picker-icon"></${icon_tag}>`;
return icon;
}
},
{
title: i18n.t('nextcloud-file-picker.filename'),
responsive: 0,
widthGrow: 5,
minWidth: 150,
field: "name",
sorter: "alphanum",
formatter: (cell) => {
let 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,
minWidth: 20,
field: "type",
formatter: (cell, formatterParams, onRendered) => {
if (typeof cell.getValue() === 'undefined') {
return "";
}
const [, fileSubType] = cell.getValue().split('/');
return fileSubType;
}
},
{
title: i18n.t('nextcloud-file-picker.last-modified'),
responsive: 3,
widthGrow: 1,
minWidth: 150,
field: "lastModified",
sorter: (a, b, aRow, bRow, column, dir, sorterParams) => {
const a_timestamp = Date.parse(a);
const b_timestamp = Date.parse(b);
return a_timestamp - b_timestamp;
},
formatter: function (cell, formatterParams, onRendered) {
const timestamp = new Date(cell.getValue());
const year = timestamp.getFullYear();
const month = ("0" + (timestamp.getMonth() + 1)).slice(-2);
const date = ("0" + timestamp.getDate()).slice(-2);
const hours = ("0" + timestamp.getHours()).slice(-2);
const minutes = ("0" + timestamp.getMinutes()).slice(-2);
return date + "." + month + "." + year + " " + hours + ":" + minutes;
}
},
{title: "file", field: "file", visible: false}
],
initialSort: [
{column: "name", dir: "asc"},
{column: "type", dir: "asc"},
],
rowClick: (e, row) => {
if (this.tabulatorTable !== null
&& this.tabulatorTable.getSelectedRows().length === this.tabulatorTable.getRows().filter(row => this.checkFileType(row.getData())).length) {
this.showSelectAllButton = false;
} else {
this.showSelectAllButton = true;
}
},
rowSelectionChanged: (data, rows) => {
if (this.tabulatorTable && this.tabulatorTable.getSelectedRows().length > 0) {
this.clipboardSelectBtnDisabled = false;
} else {
this.clipboardSelectBtnDisabled = true;
}
}
});
});
window.addEventListener('beforeunload', this._onReceiveBeforeUnload);
}
/**
* Select all files from tabulator table
*
*/
selectAll() {
this.tabulatorTable.selectRow(this.tabulatorTable.getRows().filter(row => this.checkFileType(row.getData())));
if (this.tabulatorTable.getSelectedRows().length > 0) {
this.showSelectAllButton = false;
console.log("Show Select All Button:", this.showSelectAllButton);
}
}
/**
* Deselect files from tabulator table
*
*/
deselectAll() {
this.tabulatorTable.deselectRow();
this.showSelectAllButton = true;
console.log("Show Select All Button:", this.showSelectAllButton);
}
checkFileType(file) {
// check if file is allowed
const [fileMainType, fileSubType] = file.type.split('/');
const mimeTypes = this.allowedMimeTypes.split(',');
let deny = true;
mimeTypes.forEach((str) => {
const [mainType, subType] = str.split('/');
deny = deny && ((mainType !== '*' && mainType !== fileMainType) || (subType !== '*' && subType !== fileSubType));
});
if (deny) {
console.log(`mime type ${file.type} of file '${file.name}' is not compatible with ${this.allowedMimeTypes}`);
return false;
}
return true;
}
generateClipboardTable() {
let data = [];
for (let i = 0; i < this.clipboardFiles.files.length; i++){
data[i] = {
name: this.clipboardFiles.files[i].name,
size: this.clipboardFiles.files[i].size,
type: this.clipboardFiles.files[i].type,
lastModified: this.clipboardFiles.files[i].lastModified,
file: this.clipboardFiles.files[i]
};
}
if (this.tabulatorTable !== null){
this.tabulatorTable.clearData();
this.tabulatorTable.setData(data);
}
}
async sendClipboardFiles(files) {
for(let i = 0; i < files.length; i ++)
{
await this.sendFileEvent(files[i].file);
}
this.tabulatorTable.deselectRow();
this.closeDialog();
}
async sendFileEvent(file) {
const data = {"file": file, "data": file};
const event = new CustomEvent("dbp-clipboard-file-picker-file-downloaded",
{ "detail": data, bubbles: true, composed: true });
this.dispatchEvent(event);
}
/**
* Decides if the "beforeunload" event needs to be canceled
*
* @param event
*/
onReceiveBeforeUnload(event){
// we don't need to stop if there are no signed files
if (this.clipboardFiles.files.length === 0) {
return;
}
send({
"summary": i18n.t('clipboard.file-warning'),
"body": i18n.t('clipboard.file-warning-body', {count: this.clipboardFiles.files.length}),
"type": "warning",
"timeout": 5,
});
// we need to handle custom events ourselves
if(event.target && event.target.activeElement && event.target.activeElement.nodeName) {
if (!event.isTrusted) {
// note that this only works with custom event since calls of "confirm" are ignored
// in the non-custom event, see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
const result = confirm(i18n.t('page-leaving-warn-dialogue'));
// don't stop the page leave if the user wants to leave
if (result) {
return;
}
}
// Cancel the event as stated by the standard
event.preventDefault();
// Chrome requires returnValue to be set
event.returnValue = '';
}
}
saveFilesToClipboard()
{
//save it
let data = {};
if (this.files.length !== 0) {
data = {"files": this.files};
this.sendSetPropertyEvent('clipboard-files', data);
this.closeDialog();
send({
"summary": i18n.t('file-sink.save-to-clipboard-title'),
"body": i18n.t('file-sink.save-to-clipboard-body', {count: this.files.length}),
"type": "success",
"timeout": 5,
});
console.log("--------------", this.clipboardFiles);
}
} }
getClipboardFileList() {
let files = [];
for(let i = 0; i < this.clipboardFiles.files.length; i ++)
{
files[i] = html`<div class="clipboard-list"><strong>${this.clipboardFiles.files[i].name}</strong> ${humanFileSize(this.clipboardFiles.files[i].size)}</div>`;
}
return files;
}
/**
* Open Filesink for multiple files
*/
async openClipboardFileSink() {
this._("#file-sink-clipboard").files = this.clipboardFiles.files;
this._("#file-sink-clipboard").setAttribute("dialog-open", "");
}
static get styles() { static get styles() {
// language=css // language=css
return css` return css`
...@@ -63,14 +358,201 @@ export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) { ...@@ -63,14 +358,201 @@ export class FileHandlingClipboard extends ScopedElementsMixin(DBPLitElement) {
${commonStyles.getModalDialogCSS()} ${commonStyles.getModalDialogCSS()}
${commonStyles.getRadioAndCheckboxCss()} ${commonStyles.getRadioAndCheckboxCss()}
${fileHandlingStyles.getFileHandlingCss()} ${fileHandlingStyles.getFileHandlingCss()}
.clipboard-container{
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--FUPadding, 20px);
width: 100%;
height: 100%;
position: relative;
}
.clipboard-container .wrapper{
overflow-y: auto;
text-align: center;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.clipboard-container .wrapper.table{
justify-content: start;
}
.clipboard-container .wrapper .inner{
overflow-y: auto;
text-align: center;
width: 100%;
}
.clipboard-footer{
align-self: end;
}
#select-all-wrapper{
text-align: right;
}
.clipboard-container{
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--FUPadding, 20px);
}
.clipboard-container.table{
justify-content: start;
}
.clipboard-container .inner{
overflow-y: auto;
text-align: center;
width: 100%;
}
.warning-icon{
font-size: 2rem;
padding: 0 1rem;
}
.clipboard-btn{
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.warning-container{
display: flex;
max-width: 400px;
text-align: left;
margin: auto;
}
.clipboard-data h4{
margin-top: 2rem;
}
.clipboard-data p{
margin-bottom: 1rem;
}
.clipboard-list{
padding: 1rem 0;
border-top: 1px solid #eee;
}
@media only screen
and (orientation: portrait)
and (max-device-width: 765px) {
.clipboard-container p, .clipboard-container h3{
text-align: center;
}
.warning-container{
flex-direction: column;
align-items: center;
}
.warning-icon{
margin-bottom: 1rem;
}
}
`; `;
} }
render() { render() {
return html` const tabulatorCss = commonUtils.getAssetURL(pkgName, 'tabulator-tables/css/tabulator.min.css');
HALLLOOOO if (this.clipboardSource) {
`;
return html`
<link rel="stylesheet" href="${tabulatorCss}">
<div class="block clipboard-container">
<div class="wrapper ${classMap({"table": this.clipboardFiles.files.length !== 0})}">
<div class="inner">
<h3>${i18n.t('file-source.clipboard-title')}</h3>
<p>${i18n.t('file-source.clipboard-body')}<br><br></p>
<p class="${classMap({"hidden": this.clipboardFiles.files.length !== 0})}">
${i18n.t('file-source.clipboard-no-files')}</p>
<div class="clipboard-table ${classMap({"hidden": this.clipboardFiles.files.length === 0})}">
<div id="select-all-wrapper">
<button class="button ${classMap({"hidden": !this.showSelectAllButton})}"
title="${i18n.t('nextcloud-file-picker.select-all-title')}"
@click="${() => {
this.selectAll();
}}">
${i18n.t('nextcloud-file-picker.select-all')}
</button>
<button class="button ${classMap({"hidden": this.showSelectAllButton})}"
title="${i18n.t('nextcloud-file-picker.select-nothing-title')}"
@click="${() => {
this.deselectAll();
}}">
${i18n.t('nextcloud-file-picker.select-nothing')}
</button>
</div>
<table id="clipboard-content-table" class="force-no-select"></table>
</div>
</div>
</div>
<div class="clipboard-footer ${classMap({"hidden": this.clipboardFiles.files.length === 0})}">
<button class="button select-button is-primary" ?disabled="${this.clipboardSelectBtnDisabled}"
@click="${() => {
this.sendClipboardFiles(this.tabulatorTable.getSelectedData());
}}">${i18n.t('nextcloud-file-picker.select-files')}
</button>
</div>
</div>
`;
} else {
return html`
<div class="block clipboard-container ${classMap({"table": this.clipboardFiles && this.clipboardFiles.files.length !== 0})}">
<div class="inner">
<h3>${i18n.t('file-sink.save-to-clipboard-title')}</h3>
<p>${i18n.t('file-sink.save-to-clipboard-text')}</p>
<button class="button is-primary clipboard-btn"
?disabled="${this.disabled}"
@click="${() => { this.saveFilesToClipboard(); }}">
${this.buttonLabel || i18n.t('file-sink.save-to-clipboard-btn', {count:this.clipboardFiles.files.length})}
</button>
<div class="warning-container">
<dbp-icon name="warning" class="warning-icon"></dbp-icon>
<p>${i18n.t('file-sink.save-to-clipboard-warning')}</p>
</div>
<!-- filesink for clipboard TODO übersetzen-->
<div class="${classMap({"hidden": this.clipboardFiles.files.length === 0})}">
<button id="clipboard-download-button"
class="button is-right clipboard-btn"
@click="${this.openClipboardFileSink}"
>Aktuellen Zwischenablageninhalt speichern</button>
</div>
<dbp-file-sink id="file-sink-clipboard"
context="${i18n.t('qualified-pdf-upload.save-field-label', {count: this.clipboardFiles ? this.clipboardFiles.files.length : 0})}"
filename="signed-documents.zip"
subscribe="initial-file-handling-state:initial-file-handling-state"
enabled-targets="local${this.showNextcloudFilePicker ? ",nextcloud" : ""}"
nextcloud-auth-url="${this.nextcloudWebAppPasswordURL}"
nextcloud-web-dav-url="${this.nextcloudWebDavURL}"
nextcloud-name="${this.nextcloudName}"
nextcloud-file-url="${this.nextcloudFileURL}"
lang="${this.lang}"
></dbp-file-sink>
<div class="clipboard-data ${classMap({"hidden": this.clipboardFiles.files.length === 0})}">
<h4>${i18n.t('file-sink.clipboard-files')}</h4>
<p>${i18n.t('file-sink.clipboard-files-overwrite')}</p>
${this.getClipboardFileList()}
</div>
</div>
</div>
`;
}
} }
} }
...@@ -13,6 +13,7 @@ import * as fileHandlingStyles from './styles'; ...@@ -13,6 +13,7 @@ import * as fileHandlingStyles from './styles';
import { send } from '@dbp-toolkit/common/notification'; import { send } from '@dbp-toolkit/common/notification';
import {humanFileSize} from '@dbp-toolkit/common/i18next'; import {humanFileSize} from '@dbp-toolkit/common/i18next';
import * as utils from "../../../../../src/utils"; import * as utils from "../../../../../src/utils";
import {FileHandlingClipboard} from "./dbp-file-handling-clipboard";
/** /**
...@@ -39,7 +40,6 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -39,7 +40,6 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
this.showClipboard = false; this.showClipboard = false;
this.initialFileHandlingState = {target: '', path: ''}; this.initialFileHandlingState = {target: '', path: ''};
this.clipBoardFiles = {files: ''};
} }
static get scopedElements() { static get scopedElements() {
...@@ -47,6 +47,7 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -47,6 +47,7 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
'dbp-icon': Icon, 'dbp-icon': Icon,
'dbp-mini-spinner': MiniSpinner, 'dbp-mini-spinner': MiniSpinner,
'dbp-nextcloud-file-picker': NextcloudFilePicker, 'dbp-nextcloud-file-picker': NextcloudFilePicker,
'dbp-clipboard': FileHandlingClipboard,
}; };
} }
...@@ -74,7 +75,6 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -74,7 +75,6 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
showClipboard: { type: Boolean, attribute: 'show-clipboard' }, showClipboard: { type: Boolean, attribute: 'show-clipboard' },
initialFileHandlingState: {type: Object, attribute: 'initial-file-handling-state'}, initialFileHandlingState: {type: Object, attribute: 'initial-file-handling-state'},
clipBoardFiles: {type: Object, attribute: 'clipboard-files'},
}; };
} }
...@@ -220,46 +220,54 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -220,46 +220,54 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
} }
} }
saveFilesToClipboard() closeDialog(e) {
{ this.sendDestination();
//save it MicroModal.close(this._('#modal-picker'));
let data = {};
if (this.files.length !== 0) {
data = {"files": this.files};
this.sendSetPropertyEvent('clipboard-files', data);
this.closeDialog();
send({
"summary": i18n.t('file-sink.save-to-clipboard-title'),
"body": i18n.t('file-sink.save-to-clipboard-body', {count: this.files.length}),
"type": "success",
"timeout": 5,
});
console.log("--------------", this.clipBoardFiles);
}
} }
getClipboardFiles() { getClipboardHtml() {
let files = []; if (this.enabledTargets.includes('clipboard') && this.showClipboard) {
for(let i = 0; i < this.clipBoardFiles.files.length; i ++) return html`
{ <dbp-clipboard
files[i] = html`<div class="clipboard-list"><strong>${this.clipBoardFiles.files[i].name}</strong> ${humanFileSize(this.clipBoardFiles.files[i].size)}</div>`; id="clipboard-file-sink"
subscribe="clipboard-files:clipboard-files"
lang="${this.lang}"
auth-url="${this.nextcloudAuthUrl}"
allowed-mime-types="${this.allowedMimeTypes}"
@dbp-clipboard-file-picker-file-downloaded="${(event) => {
this.sendFileEvent(event.detail.file);}}">
</dbp-clipboard>`;
} }
return files; return html``;
} }
closeDialog(e) { getNextcloudHtml() {
this.sendDestination(); if (this.enabledTargets.includes('nextcloud') && this.nextcloudWebDavUrl !== "" && this.nextcloudAuthUrl !== "") {
MicroModal.close(this._('#modal-picker')); return html`
<dbp-nextcloud-file-picker id="nextcloud-file-picker"
class="${classMap({hidden: this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}"
directories-only
max-selected-items="1"
select-button-text="${i18n.t('file-sink.select-directory')}"
?disabled="${this.disabled}"
lang="${this.lang}"
auth-url="${this.nextcloudAuthUrl}"
web-dav-url="${this.nextcloudWebDavUrl}"
nextcloud-name="${this.nextcloudName}"
directory-path="${this.nextcloudPath}"
nextcloud-file-url="${this.nextcloudFileURL}"
@dbp-nextcloud-file-picker-file-uploaded="${(event) => {
this.uploadToNextcloud(event.detail);
}}"
@dbp-nextcloud-file-picker-file-uploaded-finished="${(event) => {
this.finishedFileUpload(event);
}}">
</dbp-nextcloud-file-picker>`;
}
return html``;
} }
/**
* Open Filesink for multiple files
*/
async openClipboardFileSink() {
this._("#file-sink-clipboard").files = this.clipBoardFiles.files;
this._("#file-sink-clipboard").setAttribute("dialog-open", "");
}
static get styles() { static get styles() {
// language=css // language=css
...@@ -283,67 +291,9 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -283,67 +291,9 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
margin-bottom: 10px; margin-bottom: 10px;
} }
.clipboard-container{ #clipboard-file-sink{
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--FUPadding, 20px);
}
.clipboard-container.table{
justify-content: start;
}
.clipboard-container .inner{
overflow-y: auto;
text-align: center;
width: 100%; width: 100%;
} height: 100%;
.warning-icon{
font-size: 2rem;
padding: 0 1rem;
}
.clipboard-btn{
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.warning-container{
display: flex;
max-width: 400px;
text-align: left;
margin: auto;
}
.clipboard-data h4{
margin-top: 2rem;
}
.clipboard-data p{
margin-bottom: 1rem;
}
.clipboard-list{
padding: 1rem 0;
border-top: 1px solid #eee;
}
@media only screen
and (orientation: portrait)
and (max-device-width: 765px) {
.clipboard-container p, .clipboard-container h3{
text-align: center;
}
.warning-container{
flex-direction: column;
align-items: center;
}
.warning-icon{
margin-bottom: 1rem;
}
} }
`; `;
} }
...@@ -399,69 +349,10 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) { ...@@ -399,69 +349,10 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
</div> </div>
</div> </div>
<div class="source-main ${classMap({"hidden": this.activeTarget !== "nextcloud" || this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}"> <div class="source-main ${classMap({"hidden": this.activeTarget !== "nextcloud" || this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}">
<dbp-nextcloud-file-picker id="nextcloud-file-picker" ${this.getNextcloudHtml()}
class="${classMap({hidden: this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}"
directories-only
max-selected-items="1"
select-button-text="${i18n.t('file-sink.select-directory')}"
?disabled="${this.disabled}"
lang="${this.lang}"
auth-url="${this.nextcloudAuthUrl}"
web-dav-url="${this.nextcloudWebDavUrl}"
nextcloud-name="${this.nextcloudName}"
directory-path="${this.nextcloudPath}"
nextcloud-file-url="${this.nextcloudFileURL}"
@dbp-nextcloud-file-picker-file-uploaded="${(event) => {
this.uploadToNextcloud(event.detail);
}}"
@dbp-nextcloud-file-picker-file-uploaded-finished="${(event) => {
this.finishedFileUpload(event);
}}"></dbp-nextcloud-file-picker>
</div> </div>
<div class="source-main ${classMap({"hidden": this.activeTarget !== "clipboard" || isClipboardHidden})}"> <div class="source-main ${classMap({"hidden": this.activeTarget !== "clipboard" || isClipboardHidden})}">
<div class="block clipboard-container ${classMap({"table": this.clipBoardFiles && this.clipBoardFiles.files.length !== 0})}"> ${this.getClipboardHtml()}
<div class="inner">
<h3>${i18n.t('file-sink.save-to-clipboard-title')}</h3>
<p>${i18n.t('file-sink.save-to-clipboard-text')}</p>
<button class="button is-primary clipboard-btn"
?disabled="${this.disabled}"
@click="${() => { this.saveFilesToClipboard(); }}">
${this.buttonLabel || i18n.t('file-sink.save-to-clipboard-btn', {count:this.files.length})}
</button>
<div class="warning-container">
<dbp-icon name="warning" class="warning-icon"></dbp-icon>
<p>${i18n.t('file-sink.save-to-clipboard-warning')}</p>
</div>
<!-- filesink for clipboard TODO übersetzen-->
<div clHALLLOOOOass="${classMap({"hidden": this.clipBoardFiles.files.length === 0})}">
<button id="clipboard-download-button"
class="button is-right clipboard-btn"
@click="${this.openClipboardFileSink}"
>Aktuellen Zwischenablageninhalt speichern</button>
</div>
<dbp-file-sink id="file-sink-clipboard"
context="${i18n.t('qualified-pdf-upload.save-field-label', {count: this.clipBoardFiles ? this.clipBoardFiles.files.length : 0})}"
filename="signed-documents.zip"
subscribe="initial-file-handling-state:initial-file-handling-state"
enabled-targets="local${this.showNextcloudFilePicker ? ",nextcloud" : ""}"
nextcloud-auth-url="${this.nextcloudWebAppPasswordURL}"
nextcloud-web-dav-url="${this.nextcloudWebDavURL}"
nextcloud-name="${this.nextcloudName}"
nextcloud-file-url="${this.nextcloudFileURL}"
lang="${this.lang}"
></dbp-file-sink>
<div class="clipboard-data ${classMap({"hidden": this.clipBoardFiles.files.length === 0})}">
<h4>${i18n.t('file-sink.clipboard-files')}</h4>
<p>${i18n.t('file-sink.clipboard-files-overwrite')}</p>
${this.getClipboardFiles()}
</div>
</div>
</div>
</div> </div>
</main> </main>
</div> </div>
......
...@@ -10,9 +10,8 @@ import {NextcloudFilePicker} from "./dbp-nextcloud-file-picker"; ...@@ -10,9 +10,8 @@ import {NextcloudFilePicker} from "./dbp-nextcloud-file-picker";
import {classMap} from 'lit-html/directives/class-map.js'; import {classMap} from 'lit-html/directives/class-map.js';
import MicroModal from './micromodal.es'; import MicroModal from './micromodal.es';
import * as fileHandlingStyles from './styles'; import * as fileHandlingStyles from './styles';
import Tabulator from "tabulator-tables";
import {humanFileSize} from "@dbp-toolkit/common/i18next";
import {name as pkgName} from "../package.json"; import {name as pkgName} from "../package.json";
import {FileHandlingClipboard} from "./dbp-file-handling-clipboard";
function mimeTypesToAccept(mimeTypes) { function mimeTypesToAccept(mimeTypes) {
// Some operating systems can't handle mime types and // Some operating systems can't handle mime types and
...@@ -56,16 +55,10 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -56,16 +55,10 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
this.activeTarget = 'local'; this.activeTarget = 'local';
this.isDialogOpen = false; this.isDialogOpen = false;
this.firstOpen = true; this.firstOpen = true;
this.tabulatorTable = null;
this.maxSelectedItems = true;
this.showSelectAllButton = true;
this.clipboardSelectBtnDisabled = true;
this.showClipboard = false; this.showClipboard = false;
this.initialFileHandlingState = {target: '', path: ''}; this.initialFileHandlingState = {target: '', path: ''};
this.clipboardFiles = {files: ''};
this._onReceiveBeforeUnload = this.onReceiveBeforeUnload.bind(this);
} }
...@@ -74,6 +67,7 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -74,6 +67,7 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
'dbp-icon': Icon, 'dbp-icon': Icon,
'dbp-mini-spinner': MiniSpinner, 'dbp-mini-spinner': MiniSpinner,
'dbp-nextcloud-file-picker': NextcloudFilePicker, 'dbp-nextcloud-file-picker': NextcloudFilePicker,
'dbp-clipboard': FileHandlingClipboard,
}; };
} }
...@@ -97,13 +91,9 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -97,13 +91,9 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
decompressZip: { type: Boolean, attribute: 'decompress-zip' }, decompressZip: { type: Boolean, attribute: 'decompress-zip' },
activeTarget: { type: String, attribute: 'active-target' }, activeTarget: { type: String, attribute: 'active-target' },
isDialogOpen: { type: Boolean, attribute: 'dialog-open' }, isDialogOpen: { type: Boolean, attribute: 'dialog-open' },
showSelectAllButton: { type: Boolean, attribute: true },
clipboardSelectBtnDisabled: { type: Boolean, attribute: true },
showClipboard: { type: Boolean, attribute: 'show-clipboard' }, showClipboard: { type: Boolean, attribute: 'show-clipboard' },
initialFileHandlingState: {type: Object, attribute: 'initial-file-handling-state'}, initialFileHandlingState: {type: Object, attribute: 'initial-file-handling-state'},
clipboardFiles: {type: Object, attribute: 'clipboard-files'},
}; };
} }
...@@ -133,10 +123,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -133,10 +123,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
this.nextcloudPath = this.initialFileHandlingState.path; this.nextcloudPath = this.initialFileHandlingState.path;
} }
break; break;
case "clipboardFiles":
this.generateClipboardTable();
break;
} }
}); });
super.update(changedProperties); super.update(changedProperties);
...@@ -145,7 +131,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -145,7 +131,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
connectedCallback() { connectedCallback() {
super.connectedCallback(); super.connectedCallback();
const that = this;
this.updateComplete.then(() => { this.updateComplete.then(() => {
this.dropArea = this._('#dropArea'); this.dropArea = this._('#dropArea');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
...@@ -159,109 +144,16 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -159,109 +144,16 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
}); });
this.dropArea.addEventListener('drop', this.handleDrop.bind(this), false); this.dropArea.addEventListener('drop', this.handleDrop.bind(this), false);
this._('#fileElem').addEventListener('change', this.handleChange.bind(this)); this._('#fileElem').addEventListener('change', this.handleChange.bind(this));
// see: http://tabulator.info/docs/4.7
this.tabulatorTable = new Tabulator(this._("#clipboard-content-table"), {
layout: "fitColumns",
selectable: this.maxSelectedItems,
selectableRangeMode: "drag",
responsiveLayout: true,
placeholder: i18n.t('nextcloud-file-picker.no-data-type'),
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" class="nextcloud-picker-icon"></${icon_tag}>`;
return icon;
}},
{title: i18n.t('nextcloud-file-picker.filename'), responsive: 0, widthGrow:5, minWidth: 150, field: "name", sorter: "alphanum",
formatter: (cell) => {
let 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, minWidth: 20, field: "type", formatter: (cell, formatterParams, onRendered) => {
if (typeof cell.getValue() === 'undefined') {
return "";
}
const [, fileSubType] = cell.getValue().split('/');
return fileSubType;
}},
{title: i18n.t('nextcloud-file-picker.last-modified'), responsive: 3, widthGrow:1, minWidth: 150, field: "lastModified",sorter: (a, b, aRow, bRow, column, dir, sorterParams) => {
const a_timestamp = Date.parse(a);
const b_timestamp = Date.parse(b);
return a_timestamp - b_timestamp;
}, formatter:function(cell, formatterParams, onRendered) {
const timestamp = new Date(cell.getValue());
const year = timestamp.getFullYear();
const month = ("0" + (timestamp.getMonth() + 1)).slice(-2);
const date = ("0" + timestamp.getDate()).slice(-2);
const hours = ("0" + timestamp.getHours()).slice(-2);
const minutes = ("0" + timestamp.getMinutes()).slice(-2);
return date + "." + month + "." + year + " " + hours + ":" + minutes;
}},
{title: "file", field: "file", visible: false}
],
initialSort:[
{column:"name", dir:"asc"},
{column:"type", dir:"asc"},
],
rowClick: (e, row) => {
if (this.tabulatorTable !== null
&& this.tabulatorTable.getSelectedRows().length === this.tabulatorTable.getRows().filter(row => this.checkFileType(row.getData())).length) {
this.showSelectAllButton = false;
} else {
this.showSelectAllButton = true;
}
},
rowSelectionChanged: (data, rows) => {
if (this.tabulatorTable && this.tabulatorTable.getSelectedRows().length > 0) {
this.clipboardSelectBtnDisabled = false;
} else {
this.clipboardSelectBtnDisabled = true;
}
}
});
}); });
window.addEventListener('beforeunload', this._onReceiveBeforeUnload);
} }
disconnectedCallback() { disconnectedCallback() {
// remove event listeners
//We doesn't want to deregister this event, because we want to use this event over activities
//window.removeEventListener('beforeunload', this._onReceiveBeforeUnload);
super.disconnectedCallback(); super.disconnectedCallback();
} }
/**
* Select all files from tabulator table
*
*/
selectAll() {
this.tabulatorTable.selectRow(this.tabulatorTable.getRows().filter(row => this.checkFileType(row.getData())));
if (this.tabulatorTable.getSelectedRows().length > 0) {
this.showSelectAllButton = false;
console.log("Show Select All Button:", this.showSelectAllButton);
}
}
/**
* Deselect files from tabulator table
*
*/
deselectAll() {
this.tabulatorTable.deselectRow();
this.showSelectAllButton = true;
console.log("Show Select All Button:", this.showSelectAllButton);
}
preventDefaults (e) { preventDefaults (e) {
e.preventDefault(); e.preventDefault();
...@@ -487,8 +379,8 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -487,8 +379,8 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
} }
if (this.enabledTargets.includes('clipboard')) { if (this.enabledTargets.includes('clipboard')) {
this.generateClipboardTable(); this._("#clipboard-file-picker").generateClipboardTable();
this.showSelectAllButton = true; this._("#clipboard-file-picker").showSelectAllButton = true;
} }
const filePicker = this._('#modal-picker'); const filePicker = this._('#modal-picker');
...@@ -521,74 +413,45 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -521,74 +413,45 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
MicroModal.close(this._('#modal-picker')); MicroModal.close(this._('#modal-picker'));
} }
getClipboardHtml() {
if (this.enabledTargets.includes('clipboard') && this.showClipboard) {
generateClipboardTable() { return html`
let data = []; <dbp-clipboard
for (let i = 0; i < this.clipboardFiles.files.length; i++){ id="clipboard-file-picker"
data[i] = { clipboard-source
name: this.clipboardFiles.files[i].name, subscribe="clipboard-files:clipboard-files"
size: this.clipboardFiles.files[i].size, lang="${this.lang}"
type: this.clipboardFiles.files[i].type, auth-url="${this.nextcloudAuthUrl}"
lastModified: this.clipboardFiles.files[i].lastModified, allowed-mime-types="${this.allowedMimeTypes}"
file: this.clipboardFiles.files[i] @dbp-clipboard-file-picker-file-downloaded="${(event) => {
}; this.sendFileEvent(event.detail.file);}}">
} </dbp-clipboard>`;
if (this.tabulatorTable !== null){
this.tabulatorTable.clearData();
this.tabulatorTable.setData(data);
} }
return html``;
} }
getNextcloudHtml() {
async sendClipboardFiles(files) { if (this.enabledTargets.includes('nextcloud') && this.nextcloudWebDavUrl !== "" && this.nextcloudAuthUrl !== "") {
return html`
for(let i = 0; i < files.length; i ++) <dbp-nextcloud-file-picker id="nextcloud-file-picker"
{ class="${classMap({hidden: this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}"
await this.sendFileEvent(files[i].file); ?disabled="${this.disabled}"
lang="${this.lang}"
auth-url="${this.nextcloudAuthUrl}"
web-dav-url="${this.nextcloudWebDavUrl}"
nextcloud-name="${this.nextcloudName}"
nextcloud-file-url="${this.nextcloudFileURL}"
allowed-mime-types="${this.allowedMimeTypes}"
@dbp-nextcloud-file-picker-file-downloaded="${(event) => {
this.sendFileEvent(event.detail.file);}}">
</dbp-nextcloud-file-picker>`;
} }
this.tabulatorTable.deselectRow(); return html``;
this.closeDialog();
} }
/**
* Decides if the "beforeunload" event needs to be canceled
*
* @param event
*/
onReceiveBeforeUnload(event) {
// we don't need to stop if there are no signed files
if (!this.showClipboard || !this.hasEnabledSource("clipboard") || this.clipboardFiles.files.length === 0) {
return;
}
// we need to handle custom events ourselves
if(event.target && event.target.activeElement && event.target.activeElement.nodeName) {
if (!event.isTrusted) {
// note that this only works with custom event since calls of "confirm" are ignored
// in the non-custom event, see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
const result = confirm(i18n.t('page-leaving-warn-dialogue'));
// don't stop the page leave if the user wants to leave
if (result) {
return;
}
}
// Cancel the event as stated by the standard
event.preventDefault();
// Chrome requires returnValue to be set
event.returnValue = '';
}
}
static get styles() { static get styles() {
// language=css // language=css
return css` return css`
...@@ -625,46 +488,12 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -625,46 +488,12 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
#dropArea.highlight { #dropArea.highlight {
border-color: var(--FUBorderColorHighlight, purple); border-color: var(--FUBorderColorHighlight, purple);
} }
.clipboard-container{
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--FUPadding, 20px);
width: 100%;
height: 100%;
position: relative;
}
.clipboard-container .wrapper{
overflow-y: auto;
text-align: center;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.clipboard-container .wrapper.table{
justify-content: start;
}
.clipboard-container .wrapper .inner{ #clipboard-file-picker{
overflow-y: auto;
text-align: center;
width: 100%; width: 100%;
height: 100%;
} }
.clipboard-footer{
align-self: end;
}
#select-all-wrapper{
text-align: right;
}
@media only screen @media only screen
and (orientation: portrait) and (orientation: portrait)
and (max-device-width: 800px) { and (max-device-width: 800px) {
...@@ -673,7 +502,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -673,7 +502,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
} }
} }
`; `;
} }
...@@ -685,7 +513,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -685,7 +513,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
allowedMimeTypes += ",application/zip,application/x-zip-compressed"; allowedMimeTypes += ",application/zip,application/x-zip-compressed";
} }
const tabulatorCss = commonUtils.getAssetURL(pkgName, 'tabulator-tables/css/tabulator.min.css');
return html` return html`
<!-- <!--
...@@ -694,7 +521,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -694,7 +521,6 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
@click="${() => { this.openDialog(); }}">${i18n.t('file-source.open-menu')}</button> @click="${() => { this.openDialog(); }}">${i18n.t('file-source.open-menu')}</button>
--> -->
<div class="modal micromodal-slide" id="modal-picker" aria-hidden="true"> <div class="modal micromodal-slide" id="modal-picker" aria-hidden="true">
<link rel="stylesheet" href="${tabulatorCss}">
<div class="modal-overlay" tabindex="-1" data-micromodal-close> <div class="modal-overlay" tabindex="-1" data-micromodal-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">
...@@ -744,48 +570,10 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) { ...@@ -744,48 +570,10 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
</div> </div>
</div> </div>
<div class="source-main ${classMap({"hidden": this.activeTarget !== "nextcloud" || this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}"> <div class="source-main ${classMap({"hidden": this.activeTarget !== "nextcloud" || this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}">
<dbp-nextcloud-file-picker id="nextcloud-file-picker" ${this.getNextcloudHtml()}
class="${classMap({hidden: this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}"
?disabled="${this.disabled}"
lang="${this.lang}"
auth-url="${this.nextcloudAuthUrl}"
web-dav-url="${this.nextcloudWebDavUrl}"
nextcloud-name="${this.nextcloudName}"
nextcloud-file-url="${this.nextcloudFileURL}"
allowed-mime-types="${this.allowedMimeTypes}"
@dbp-nextcloud-file-picker-file-downloaded="${(event) => {
this.sendFileEvent(event.detail.file);
}}"></dbp-nextcloud-file-picker>
</div> </div>
<div class="source-main ${classMap({"hidden": (this.activeTarget !== "clipboard" || isClipboardHidden)})}"> <div class="source-main ${classMap({"hidden": (this.activeTarget !== "clipboard" || isClipboardHidden)})}">
<div class="block clipboard-container"> ${this.getClipboardHtml()}
<div class="wrapper ${classMap({"table": this.clipboardFiles.files.length !== 0})}">
<div class="inner">
<h3>${i18n.t('file-source.clipboard-title')}</h3>
<p>${i18n.t('file-source.clipboard-body')}<br><br></p>
<p class="${classMap({"hidden": this.clipboardFiles.files.length !== 0})}">${i18n.t('file-source.clipboard-no-files')}</p>
<div class="clipboard-table ${classMap({"hidden": this.clipboardFiles.files.length === 0})}">
<div id="select-all-wrapper">
<button class="button ${classMap({"hidden": !this.showSelectAllButton})}"
title="${i18n.t('nextcloud-file-picker.select-all-title')}"
@click="${() => { this.selectAll(); }}">
${i18n.t('nextcloud-file-picker.select-all')}
</button>
<button class="button ${classMap({"hidden": this.showSelectAllButton})}"
title="${i18n.t('nextcloud-file-picker.select-nothing-title')}"
@click="${() => { this.deselectAll(); }}">
${i18n.t('nextcloud-file-picker.select-nothing')}
</button>
</div>
<table id="clipboard-content-table" class="force-no-select"></table>
</div>
</div>
</div>
<div class="clipboard-footer ${classMap({"hidden": this.clipboardFiles.files.length === 0 })}">
<button class="button select-button is-primary" ?disabled="${ this.clipboardSelectBtnDisabled }"
@click="${() => { this.sendClipboardFiles(this.tabulatorTable.getSelectedData()); }}">${i18n.t('nextcloud-file-picker.select-files')}</button>
</div>
</div>
</div> </div>
</main> </main>
</div> </div>
......
...@@ -96,5 +96,10 @@ ...@@ -96,5 +96,10 @@
"select-nothing-title": "Alle gewählten Dateien nicht mehr selektieren", "select-nothing-title": "Alle gewählten Dateien nicht mehr selektieren",
"abort": "Vorgang abbrechen", "abort": "Vorgang abbrechen",
"abort-message": "Vorgang wurde abgebrochen." "abort-message": "Vorgang wurde abgebrochen."
},
"clipboard": {
"file-warning": "Achtung!",
"file-warning-body": "Es befindet sich noch eine Datei in der Zwischenablage. Die Zwischenablage wird beim Verlassen der Seite automatisch verworfen.",
"file-warning-body_plural": "Es befinden sich noch {{count}} Dateien in der Zwischenablage. Die Zwischenablage wird beim Verlassen der Seite automatisch verworfen."
} }
} }
...@@ -96,5 +96,10 @@ ...@@ -96,5 +96,10 @@
"select-nothing-title": "Select no files", "select-nothing-title": "Select no files",
"abort": "Cancel process", "abort": "Cancel process",
"abort-message": "The process was canceled." "abort-message": "The process was canceled."
},
"clipboard": {
"file-warning": "Attention!",
"file-warning-body": "There is still a file on the clipboard. The clipboard is automatically discarded when you exit the page.",
"file-warning-body_plural": "There are still {{count}} files on the clipboard. The clipboard is automatically discarded when you exit the page."
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment