UNPKG

@digital-blueprint/lunchlottery-app

Version:

[GitHub Repository](https://github.com/digital-blueprint/lunchlottery-app) | [npmjs package](https://www.npmjs.com/package/@digital-blueprint/lunchlottery-app) | [Unpkg CDN](https://unpkg.com/browse/@digital-blueprint/lunchlottery-app/)

1,219 lines (1,122 loc) 140 kB
import {createInstance} from './i18n'; import {css, html} from 'lit'; import {ScopedElementsMixin, LangMixin} from '@dbp-toolkit/common'; import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element'; import {Icon, MiniSpinner} from '@dbp-toolkit/common'; import * as commonUtils from '@dbp-toolkit/common/utils'; import * as commonStyles from '@dbp-toolkit/common/styles'; import * as tabulatorStyles from '@dbp-toolkit/tabulator-table/src/tabulator-table-styles'; import {createClient, parseXML} from 'webdav/web'; import {classMap} from 'lit/directives/class-map.js'; import {humanFileSize} from '@dbp-toolkit/common/i18next'; import {TabulatorFull as Tabulator} from 'tabulator-tables'; import MicroModal from './micromodal.es'; import {name as pkgName} from './../package.json'; import * as fileHandlingStyles from './styles'; import {encrypt, decrypt, parseJwt} from './crypto.js'; /** * A wrapper for the webdav putFileContents method that also handles Blob/File * (inefficiently, but works). In theory this could be fixed upstream, if someone * feels strongly about it. * * https://github.com/perry-mitchell/webdav-client#putfilecontents */ async function customPutFileContents(client, filename, data, options = {}) { if (data instanceof Blob) { // Broke with 4.x // contentLength: https://github.com/perry-mitchell/webdav-client/issues/266 if (options.contentLength === true || options.contentLength === undefined) { options.contentLength = data.size; } // Broke with 5.x // Blob: https://github.com/perry-mitchell/webdav-client/issues/352 data = await data.arrayBuffer(); } return client.putFileContents(filename, data, options); } /** * NextcloudFilePicker web component */ export class NextcloudFilePicker extends LangMixin( ScopedElementsMixin(DBPLitElement), createInstance, ) { constructor() { super(); this.auth = {}; this.authUrl = ''; this.webDavUrl = ''; this.fullWebDavUrl = ''; this.nextcloudName = 'Nextcloud'; this.nextcloudFileURL = ''; this.loginWindow = null; this.isPickerActive = false; this.statusText = ''; this.lastDirectoryPath = '/'; this.directoryPath = ''; this.webDavClient = null; this.tabulatorTable = null; this.allowedMimeTypes = ''; this.directoriesOnly = false; this.maxSelectedItems = Number.MAX_VALUE; this.loading = false; this._onReceiveWindowMessage = this.onReceiveWindowMessage.bind(this); this.folderIsSelected = this._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 = 'RGDNVCK'; this.activeDirectoryACL = ''; this.forAll = false; this.uploadCount = 0; this.abortUploadButton = false; this.abortUpload = false; this.authInfo = ''; this.selectBtnDisabled = true; this.storeSession = false; this.boundCloseAdditionalMenuHandler = this.hideAdditionalMenu.bind(this); this.initateOpenAdditionalMenu = false; this.isInFavorites = false; this.isInFilteredRecent = false; this.isInRecent = false; this.userName = ''; this.menuHeightBreadcrumb = -1; this.boundCloseBreadcrumbMenuHandler = this.hideBreadcrumbMenu.bind(this); this.initateOpenBreadcrumbMenu = false; this.boundClickOutsideNewFolderHandler = this.deleteNewFolderEntry.bind(this); this.initateOpenNewFolder = false; this.disableRowClick = false; this.boundRefreshOnWindowSizeChange = this.refreshOnWindowSizeChange.bind(this); this.boundCancelNewFolderHandler = this.cancelNewFolder.bind(this); this.boundSelectHandler = this.selectAllFiles.bind(this); } static get scopedElements() { return { 'dbp-icon': Icon, 'dbp-mini-spinner': MiniSpinner, }; } /** * See: https://lit-element.polymer-project.org/guide/properties#initialize */ static get properties() { return { ...super.properties, auth: {type: Object}, authUrl: {type: String, attribute: 'auth-url'}, webDavUrl: {type: String, attribute: 'web-dav-url'}, fullWebDavUrl: {type: String, attribute: false}, nextcloudFileURL: {type: String, attribute: 'nextcloud-file-url'}, nextcloudName: {type: String, attribute: 'nextcloud-name'}, isPickerActive: {type: Boolean, attribute: false}, statusText: {type: String, attribute: false}, folderIsSelected: {type: String, attribute: false}, authInfo: {type: String, attribute: 'auth-info'}, directoryPath: {type: String, attribute: 'directory-path'}, allowedMimeTypes: {type: String, attribute: 'allowed-mime-types'}, directoriesOnly: {type: Boolean, attribute: 'directories-only'}, 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}, abortUploadButton: {type: Boolean, attribute: false}, selectBtnDisabled: {type: Boolean, attribute: true}, userName: {type: Boolean, attribute: false}, storeSession: {type: Boolean, attribute: 'store-nextcloud-session'}, disableRowClick: {type: Boolean, attribute: false}, }; } update(changedProperties) { changedProperties.forEach((oldValue, propName) => { switch (propName) { case 'auth': this._updateAuth(); break; case 'directoriesOnly': if (this.directoriesOnly && this._('#select_all_wrapper')) { this._('#select_all_wrapper').classList.remove('button-container'); this._('#select_all_wrapper').classList.add('hidden'); } if (!this.directoriesOnly && this._('#select_all_wrapper')) { this._('#select_all_wrapper').classList.add('button-container'); this._('#select_all_wrapper').classList.remove('hidden'); } break; } }); super.update(changedProperties); } disconnectedCallback() { window.removeEventListener('message', this._onReceiveWindowMessage); window.removeEventListener('resize', this.boundRefreshOnWindowSizeChange); // deregister tabulator table callback events this.tabulatorTable.off('tableBuilt'); this.tabulatorTable.off('rowSelectionChanged'); this.tabulatorTable.off('rowClick'); this.tabulatorTable.off('rowAdded'); this.tabulatorTable.off('dataLoaded'); super.disconnectedCallback(); } connectedCallback() { super.connectedCallback(); const i18n = this._i18n; this._loginStatus = ''; this._loginState = []; this._loginCalled = false; this.updateComplete.then(() => { // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage window.addEventListener('message', this._onReceiveWindowMessage); // see: http://tabulator.info/docs/5.1 this.tabulatorTable = new Tabulator(this._('#directory-content-table'), { layout: 'fitColumns', selectableRows: this.maxSelectedItems, placeholder: this.directoriesOnly ? i18n.t('nextcloud-file-picker.no-data') : i18n.t('nextcloud-file-picker.no-data-type'), responsiveLayout: 'collapse', responsiveLayoutCollapseStartOpen: false, columnHeaderVertAlign: 'middle', columnDefaults: { vertAlign: 'middle', hozAlign: 'left', resizable: false, }, columns: [ { minWidth: 40, headerSort: false, formatter: 'responsiveCollapse', }, { title: '<label id="select_all_wrapper" class="button-container select-all-icon">' + '<input type="checkbox" id="select_all" name="select_all" value="select_all">' + '<span class="checkmark" id="select_all_checkmark"></span>' + '</label>', field: 'type', hozAlign: 'center', width: 40, headerSort: false, responsive: 1, formatter: (cell, formatterParams, onRendered) => { let disabled = this.directoriesOnly ? 'nextcloud-picker-icon-disabled' : ''; let icon = `<dbp-icon name="empty-file" class="nextcloud-picker-icon ` + disabled + `"></dbp-icon>`; let html = cell.getValue() === 'directory' ? `<dbp-icon name="folder" class="nextcloud-picker-icon"></dbp-icon>` : icon; let div = this.createScopedElement('div'); div.innerHTML = html; return div; }, }, { title: i18n.t('nextcloud-file-picker.filename'), responsive: 0, widthGrow: 5, minWidth: 150, field: 'basename', sorter: 'alphanum', formatter: (cell) => { let div = this.createScopedElement('div'); div.classList.add('filename'); div.innerHTML = cell.getValue(); return div; }, }, { title: i18n.t('nextcloud-file-picker.size'), responsive: 4, widthGrow: 1, minWidth: 84, 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: 58, field: 'mime', 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: 'lastmod', 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 d = Date.parse(cell.getValue()); const timestamp = new Date(d); 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: 'rights', field: 'props.permissions', visible: false}, {title: 'acl', field: 'props.acl-list.acl.acl-permissions', visible: false}, ], initialSort: [ {column: 'basename', dir: 'asc'}, {column: 'type', dir: 'asc'}, ], rowFormatter: (row) => { let data = row.getData(); if (!this.checkFileType(data, this.allowedMimeTypes)) { row.getElement().classList.add('no-select'); // TODO check this row.getElement().classList.remove('tabulator-selectable'); } if (this.directoriesOnly && typeof data.mime !== 'undefined') { row.getElement().classList.add('no-select'); row.getElement().classList.remove('tabulator-selectable'); } if (!this.directoriesOnly && typeof data.mime === 'undefined') { row.getElement().classList.add('no-select-styles'); } }, }); this.tabulatorTable.on('tableBuilt', this.tableBuiltFunction.bind(this)); this.tabulatorTable.on( 'rowSelectionChanged', this.rowSelectionChangedFunction.bind(this), ); this.tabulatorTable.on('rowClick', this.rowClickFunction.bind(this)); this.tabulatorTable.on('rowAdded', this.rowAddedFunction.bind(this)); this.tabulatorTable.on('dataLoaded', this.dataLoadedFunction.bind(this)); if ( typeof this.allowedMimeTypes !== 'undefined' && this.allowedMimeTypes !== '' && !this.directoriesOnly ) { this.tabulatorTable.setFilter(this.checkFileType, this.allowedMimeTypes); } window.addEventListener('resize', this.boundRefreshOnWindowSizeChange); }); } tableBuiltFunction() { if (this._('#select_all')) { this._('#select_all').addEventListener('click', this.boundSelectHandler); } if (this.directoriesOnly && this._('#select_all_wrapper')) { this._('#select_all_wrapper').classList.remove('button-container'); this._('#select_all_wrapper').classList.add('hidden'); } } rowSelectionChangedFunction(data, rows) { const i18n = this._i18n; if (!this.disableRowClick) { if (data.length > 0 && this.directoriesOnly) { this.folderIsSelected = i18n.t('nextcloud-file-picker.load-to-folder'); } else { this.folderIsSelected = i18n.t('nextcloud-file-picker.load-in-folder'); } if ( !this.directoriesOnly && this.tabulatorTable && this.tabulatorTable .getSelectedRows() .filter( (row) => row.getData().type != 'directory' && this.checkFileType(row.getData(), this.allowedMimeTypes), ).length > 0 ) { this.selectBtnDisabled = false; } else { this.selectBtnDisabled = true; } if (this._('#select_all_checkmark')) { this._('#select_all_checkmark').title = this.checkAllSelected() ? i18n.t('clipboard.select-nothing') : i18n.t('clipboard.select-all'); } this.requestUpdate(); } } rowClickFunction(e, row) { const i18n = this._i18n; const data = row.getData(); if (!row.getElement().classList.contains('no-select') && !this.disableRowClick) { 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 { switch (data.type) { case 'directory': this.directoryClicked(e, data); break; case 'file': if ( this.tabulatorTable !== null && this.tabulatorTable.getSelectedRows().length === this.tabulatorTable .getRows() .filter( (row) => row.getData().type != 'directory' && this.checkFileType( row.getData(), this.allowedMimeTypes, ), ).length ) { this._('#select_all').checked = true; } else { this._('#select_all').checked = false; } break; } } } else { row.deselect(); } } rowAddedFunction(row) { if (!this.disableRowClick) { row.getElement().classList.toggle('addRowAnimation'); } } dataLoadedFunction(data) { if (this.tabulatorTable !== null) { const that = this; setTimeout(function () { if (that._('.tabulator-responsive-collapse-toggle-open')) { that._a('.tabulator-responsive-collapse-toggle-open').forEach((element) => element.addEventListener('click', that.toggleCollapse.bind(that)), ); } if (that._('.tabulator-responsive-collapse-toggle-close')) { that._a('.tabulator-responsive-collapse-toggle-close').forEach((element) => element.addEventListener('click', that.toggleCollapse.bind(that)), ); } }, 0); } } /** * Request a re-render every time isLoggedIn()/isLoading() changes */ _updateAuth() { this._loginStatus = this.auth['login-status']; let newLoginState = [this.isLoggedIn(), this.isLoading()]; if (this._loginState.toString() !== newLoginState.toString()) { this.requestUpdate(); } this._loginState = newLoginState; if (this.isLoggedIn() && !this._loginCalled) { this._loginCalled = true; this.checkLocalStorage(); } } /** * Returns if a person is set in or not * * @returns {boolean} true or false */ isLoggedIn() { return this.auth && this.auth.token; } /** * Returns true if a person has successfully logged in * * @returns {boolean} true or false */ isLoading() { if (this._loginStatus === 'logged-out') return false; return !this.isLoggedIn() && this.auth.token !== undefined; } /** * */ async checkLocalStorage() { if (!this.isLoggedIn() || !this.auth) { for (let key of Object.keys(localStorage)) { if ( key.includes('nextcloud-webdav-username-') || key.includes('nextcloud-webdav-password-') || key.includes('nextcloud-webdav-url-') ) { localStorage.removeItem(key); } } return; } const publicId = this.auth['user-id']; const token = this.auth.token ? parseJwt(this.auth.token) : null; const sessionId = token ? token.sid : ''; if ( this.storeSession && sessionId && localStorage.getItem('nextcloud-webdav-username-' + publicId) && localStorage.getItem('nextcloud-webdav-password-' + publicId) && localStorage.getItem('nextcloud-webdav-url-' + publicId) ) { try { const userName = await decrypt( sessionId, localStorage.getItem('nextcloud-webdav-username-' + publicId), ); const password = await decrypt( sessionId, localStorage.getItem('nextcloud-webdav-password-' + publicId), ); this.fullWebDavUrl = await decrypt( sessionId, localStorage.getItem('nextcloud-webdav-url-' + publicId), ); this.webDavClient = createClient(this.fullWebDavUrl, { username: userName, password: password, }); this.userName = userName; this.isPickerActive = true; this.loadDirectory(this.directoryPath); } catch { localStorage.removeItem('nextcloud-webdav-username-' + publicId); localStorage.removeItem('nextcloud-webdav-password-' + publicId); localStorage.removeItem('nextcloud-webdav-url-' + publicId); return; } } } /** * check mime type of row * * @param data * @param filterParams */ checkFileType(data, filterParams) { if (filterParams === '') return true; if (typeof data.mime === 'undefined') { return true; } const [fileMainType, fileSubType] = data.mime.split('/'); const mimeTypes = filterParams.split(','); let deny = true; mimeTypes.forEach((str) => { const [mainType, subType] = str.split('/'); deny = deny && ((mainType !== '*' && mainType !== fileMainType) || (subType !== '*' && subType !== fileSubType)); }); return !deny; } async openFilePicker() { const i18n = this._i18n; this.disableRowClick = false; 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', 'width=400,height=400,menubar=no,scrollbars=no,status=no,titlebar=no,toolbar=no', ); } else { this.loadDirectory(this.directoryPath, this.webDavClient); } } _a(selector) { return this.renderRoot.querySelectorAll(selector); } async onReceiveWindowMessage(event) { if (this.webDavClient === null) { const data = event.data; if (data.type === 'webapppassword') { if (this.loginWindow !== null) { this.loginWindow.close(); } // See https://github.com/perry-mitchell/webdav-client/blob/master/API.md#module_WebDAV.createClient this.webDavClient = createClient( data.webdavUrl || this.webDavUrl + '/' + data.loginName, { username: data.loginName, password: data.token, }, ); this.fullWebDavUrl = data.webdavUrl; if ( this.storeSession && this.isLoggedIn() && this._('#remember-checkbox') && this._('#remember-checkbox').checked ) { const publicId = this.auth['user-id']; const token = this.auth.token ? parseJwt(this.auth.token) : null; const sessionId = token ? token.sid : ''; if (sessionId) { const encrytedName = await encrypt(sessionId, data.loginName); const encrytedToken = await encrypt(sessionId, data.token); const encryptedUrl = await encrypt(sessionId, data.webdavUrl); localStorage.setItem('nextcloud-webdav-username-' + publicId, encrytedName); localStorage.setItem( 'nextcloud-webdav-password-' + publicId, encrytedToken, ); localStorage.setItem('nextcloud-webdav-url-' + publicId, encryptedUrl); } } this.loadDirectory(this.directoryPath); this.userName = data.loginName; } } } toggleCollapse(e) { const table = this.tabulatorTable; // give a chance to draw the table // this is for getting more hight in tabulator table, when toggle is called setTimeout(function () { table.redraw(); }, 0); } /** * * @param {*} data * @returns {Array} reduced list of objects, including users files */ filterUserFilesOnly(data) { // R = Share, S = Shared Folder, M = Group folder or external source, G = Read, D = Delete, NV / NVW = Write, CK = Create let result = []; for (let i = 0; i < data.length; i++) { if (data) { let file_perm = data[i].props.permissions; if (!file_perm.includes('M') && !file_perm.includes('S')) { result.push(data[i]); } } } return result; } /** * * @param {*} path * @returns {Array} including file path and base name */ parseFileAndBaseName(path) { if (path[0] !== '/') { //TODO verify path = '/' + path; } while (/^.+\/$/.test(path)) { path = path.substr(0, path.length - 1); } path = decodeURIComponent(path); let array1 = this.webDavUrl.split('/'); let array2 = path.split('/'); for (let i = 0; i < array2.length; i++) { let item2 = array2[i]; array1.forEach((item1) => { if (item1 === item2) { array2.shift(); i--; } }); } array2.shift(); let basename = array2[array2.length - 1]; let filename = '/' + array2.join('/'); return [filename, basename]; } /** * * @param {*} response * @returns {Array} list of file objects containing corresponding information */ mapResponseToObject(response) { let results = []; response.forEach((item) => { const [filePath, baseName] = this.parseFileAndBaseName(item.href); const prop = item.propstat.prop; let etag = typeof prop.getetag === 'string' ? prop.getetag.replace(/"/g, '') : null; let sizeVal = prop.getcontentlength ? prop.getcontentlength : '0'; let fileType = prop.resourcetype && typeof prop.resourcetype === 'object' && typeof prop.resourcetype.collection !== 'undefined' ? 'directory' : 'file'; let mimeType; if (fileType === 'file') { mimeType = prop.getcontenttype && typeof prop.getcontenttype === 'string' ? prop.getcontenttype.split(';')[0] : ''; } let propsObject = { getetag: etag, getlastmodified: prop.getlastmodified, getcontentlength: sizeVal, permissions: prop.permissions, resourcetype: fileType, getcontenttype: prop.getcontenttype, }; let statObject = { basename: baseName, etag: etag, filename: filePath, lastmod: prop.getlastmodified, mime: mimeType, props: propsObject, size: parseInt(sizeVal, 10), type: fileType, }; results.push(statObject); }); return results; } /** * Loads the favorites from WebDAV * */ loadFavorites() { this.hideAdditionalMenu(); const i18n = this._i18n; if (typeof this.directoryPath === 'undefined' || this.directoryPath === undefined) { this.directoryPath = ''; } console.log('load nextcloud favorites'); this.selectAllButton = true; this.loading = true; this.statusText = i18n.t('nextcloud-file-picker.loadpath-nextcloud-file-picker', { name: this.nextcloudName, }); this.lastDirectoryPath = this.directoryPath; this.directoryPath = ''; this.isInRecent = false; this.isInFilteredRecent = false; this.isInFavorites = true; 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; } //see https://github.com/perry-mitchell/webdav-client#customRequest this.webDavClient .customRequest('/', { method: 'REPORT', responseType: 'text/xml', details: true, data: '<?xml version="1.0"?>' + ' <oc:filter-files xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">' + ' <oc:filter-rules>' + ' <oc:favorite>1</oc:favorite>' + ' </oc:filter-rules>' + ' <d:prop>' + ' <d:getlastmodified />' + ' <d:resourcetype />' + ' <d:getcontenttype />' + ' <d:getcontentlength />' + ' <d:getetag />' + ' <oc:permissions />' + ' </d:prop>' + ' </oc:filter-files>', }) .then((contents) => { parseXML(contents.data).then((davResp) => { // console.log("-contents.data-----", davResp); let dataObject = this.mapResponseToObject(davResp.multistatus.response); this.loading = false; this.statusText = ''; this.tabulatorTable.setData(dataObject); this.tabulatorTable.setSort([ {column: 'basename', dir: 'asc'}, {column: 'type', dir: 'asc'}, ]); if (this._('.tabulator-placeholder-contents')) { this._('.tabulator-placeholder-contents').innerText = i18n.t( 'nextcloud-file-picker.no-favorites', { name: this.nextcloudName, }, ); } this.isPickerActive = true; this._('.nextcloud-content').scrollTop = 0; this._('#download-button').setAttribute('disabled', 'true'); }); }) .catch((error) => { //TODO verify error catching console.error(error.message); // on Error: try to reload with home directory if (this.webDavClient !== null && error.message.search('401') === -1) { console.log('error in load directory'); this.directoryPath = ''; this.loadDirectory(''); } else { this.loading = false; this.statusText = html` <span class="error"> ${i18n.t('nextcloud-file-picker.webdav-error', {error: error.message})} </span> `; this.isPickerActive = false; 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.isInFavorites = false; }); } /** * Loads recent files and folders from WebDAV * */ loadAllRecentFiles() { this.hideAdditionalMenu(); const i18n = this._i18n; if (typeof this.directoryPath === 'undefined' || this.directoryPath === undefined) { this.directoryPath = ''; } console.log('load recent files'); this.selectAllButton = true; this.loading = true; this.statusText = i18n.t('nextcloud-file-picker.loadpath-nextcloud-file-picker', { name: this.nextcloudName, }); this.lastDirectoryPath = this.directoryPath; this.directoryPath = ''; this.isInFavorites = false; this.isInFilteredRecent = false; this.isInRecent = true; let date = new Date(); date.setMonth(date.getMonth() - 3); let searchDate = date.toISOString().split('.')[0] + 'Z'; if (this.webDavClient === null || this.userName === 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; } //see https://github.com/perry-mitchell/webdav-client#customRequest this.webDavClient .customRequest('../..', { method: 'SEARCH', responseType: 'text/xml', headers: {'Content-Type': 'text/xml'}, details: true, data: '<?xml version="1.0" encoding=\'UTF-8\'?>' + ' <d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">' + ' <d:basicsearch>' + ' <d:select>' + ' <d:prop>' + ' <d:getlastmodified />' + ' <d:resourcetype />' + ' <d:getcontenttype />' + ' <d:getcontentlength />' + ' <d:getetag />' + ' <oc:permissions />' + ' <oc:size/>' + ' <oc:owner-id/>' + ' <oc:owner-display-name/>' + ' </d:prop>' + ' </d:select>' + ' <d:from>' + ' <d:scope>' + ' <d:href>' + this.fullWebDavUrl + '</d:href>' + ' <d:depth>infinity</d:depth>' + ' </d:scope>' + ' </d:from>' + ' <d:where> ' + ' <d:and>' + ' <d:gte>' + ' <d:prop>' + ' <d:getlastmodified/>' + ' </d:prop>' + ' <d:literal>' + searchDate + '</d:literal>' + ' </d:gte>' + ' <d:or>' + this.getMimeTypes() + ' </d:or>' + ' </d:and>' + ' </d:where>' + ' <d:orderby>' + ' <d:order>' + ' <d:prop>' + ' <d:getlastmodified/>' + ' </d:prop>' + ' <d:descending/>' + ' </d:order>' + ' </d:orderby>' + ' <d:limit>' + ' <d:nresults>15</d:nresults>' + ' </d:limit>' + ' </d:basicsearch>' + ' </d:searchrequest>', }) .then((contents) => { parseXML(contents.data).then((davResp) => { // console.log('davResp', davResp); let dataObject = this.mapResponseToObject(davResp.multistatus.response); // console.log("-contents.data-----", dataObject); this.loading = false; this.statusText = ''; this.tabulatorTable.setData(dataObject); this.tabulatorTable.setSort([{column: 'lastmod', dir: 'desc'}]); if (this._('.tabulator-placeholder-contents')) { this._('.tabulator-placeholder-contents').innerText = i18n.t( 'nextcloud-file-picker.no-recent-files', { name: this.nextcloudName, }, ); } this.isPickerActive = true; this._('.nextcloud-content').scrollTop = 0; this._('#download-button').setAttribute('disabled', 'true'); }); }) .catch((error) => { console.error(error.message); // on Error: try to reload with home directory if (this.webDavClient !== null && error.message.search('401') === -1) { console.log('error in load directory'); this.directoryPath = ''; this.loadDirectory(''); } else { this.loading = false; this.statusText = html` <span class="error"> ${i18n.t('nextcloud-file-picker.webdav-error', {error: error.message})} </span> `; this.isPickerActive = false; 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.isInRecent = false; }); } getMimeTypes() { let mimePart = ''; if ( this.allowedMimeTypes && this.allowedMimeTypes !== 0 && this.allowedMimeTypes !== '*/*' ) { const mimeTypes = this.allowedMimeTypes.split(','); mimeTypes.forEach((str) => { mimePart += ' <d:like>' + ' <d:prop>' + ' <d:getcontenttype/>' + ' </d:prop>' + ' <d:literal>' + str + '</d:literal>' + ' </d:like>'; }); } else { mimePart = ' <d:like>' + ' <d:prop>' + ' <d:getcontenttype/>' + ' </d:prop>' + ' <d:literal>application/%</d:literal>' + ' </d:like>' + ' <d:like>' + ' <d:prop>' + ' <d:getcontenttype/>' + ' </d:prop>' + ' <d:literal>text/%</d:literal>' + ' </d:like>' + ' <d:like>' + ' <d:prop>' + ' <d:getcontenttype/>' + ' </d:prop>' + ' <d:literal>image/%</d:literal>' + ' </d:like>' + ' <d:like>' + ' <d:prop>' + ' <d:getcontenttype/>' + ' </d:prop>' + ' <d:literal>video/%</d:literal>' + ' </d:like>' + ' <d:like>' + ' <d:prop>' + ' <d:getcontenttype/>' + ' </d:prop>' + ' <d:literal>audio/%</d:literal>' + ' </d:like>' + ' <d:like>' + ' <d:prop>' + ' <d:getcontenttype/>' + ' </d:prop>' + ' <d:literal>font/%</d:literal>' + ' </d:like>'; } return mimePart; } /** * Loads recent files and folders from WebDAV * */ loadMyRecentFiles() { this.hideAdditionalMenu(); const i18n = this._i18n; if (typeof this.directoryPath === 'undefined' || this.directoryPath === undefined) { this.directoryPath = ''; } console.log('load only my recent files'); this.selectAllButton = true; this.loading = true; this.statusText = i18n.t('nextcloud-file-picker.loadpath-nextcloud-file-picker', { name: this.nextcloudName, }); this.lastDirectoryPath = this.directoryPath; this.directoryPath = ''; this.isInFavorites = false; this.isInRecent = false; this.isInFilteredRecent = true; let date = new Date(); date.setMonth(date.getMonth() - 3); let searchDate = date.toISOString().split('.')[0] + 'Z'; if (this.webDavClient === null || this.userName === 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; } //see https://github.com/perry-mitchell/webdav-client#customRequest this.webDavClient .customRequest('../..', { method: 'SEARCH', responseType: 'text/xml', headers: {'Content-Type': 'text/xml'}, details: true, data: '<?xml version="1.0" encoding=\'UTF-8\'?>' + ' <d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">' + ' <d:basicsearch>' + ' <d:select>' + ' <d:prop>' + ' <d:getlastmodified />' + ' <d:resourcetype />' + ' <d:getcontenttype />' + ' <d:getcontentlength />' + ' <d:getetag />' + ' <oc:permissions />' + ' <oc:size/>' + ' <oc:owner-id/>' + ' <oc:owner-display-name/>' + ' </d:prop>' + ' </d:select>' + ' <d:from>' + ' <d:scope>' + ' <d:href>' + this.fullWebDavUrl + '</d:href>' + ' <d:depth>infinity</d:depth>' + ' </d:scope>' + ' </d:from>' + ' <d:where> ' + ' <d:and>' + ' <d:gte>' + ' <d:prop>' + ' <d:getlastmodified/>' + ' </d:prop>' + ' <d:literal>' + searchDate + '</d:literal>' + ' </d:gte>' + ' <d:or>' + this.getMimeTypes() + ' </d:or>' + ' </d:and>' + ' </d:where>' + ' <d:orderby>' + ' <d:order>' + ' <d:prop>' + ' <d:getlastmodified/>' + ' </d:prop>' + ' <d:descending/>' + ' </d:order>' +