diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files_versions/src/components/Version.vue | 59 | ||||
-rw-r--r-- | apps/files_versions/src/utils/davRequest.js | 1 | ||||
-rw-r--r-- | apps/files_versions/src/utils/versions.js | 63 | ||||
-rw-r--r-- | apps/files_versions/src/views/VersionTab.vue | 62 |
4 files changed, 147 insertions, 38 deletions
diff --git a/apps/files_versions/src/components/Version.vue b/apps/files_versions/src/components/Version.vue index 93dfb95b8b7..28b831ac37a 100644 --- a/apps/files_versions/src/components/Version.vue +++ b/apps/files_versions/src/components/Version.vue @@ -19,13 +19,13 @@ <div> <NcListItem class="version" :title="versionLabel" - :href="downloadURL" :force-display-actions="true" - data-files-versions-version> + data-files-versions-version + @click="click"> <template #icon> <div v-if="!(loadPreview || previewLoaded)" class="version__image" /> <img v-else-if="isCurrent || version.hasPreview" - :src="previewURL" + :src="version.previewUrl" alt="" decoding="async" fetchpriority="low" @@ -46,7 +46,7 @@ </div> </template> <template #actions> - <NcActionButton v-if="enableLabeling" + <NcActionButton v-if="enableLabeling" :close-after-click="true" @click="openVersionLabelModal"> <template #icon> @@ -54,6 +54,14 @@ </template> {{ version.label === '' ? t('files_versions', 'Name this version') : t('files_versions', 'Edit version name') }} </NcActionButton> + <NcActionButton v-if="!isCurrent && canView && canCompare" + :close-after-click="true" + @click="compareVersion"> + <template #icon> + <FileCompare :size="22" /> + </template> + {{ t('files_versions', 'Compare to current version') }} + </NcActionButton> <NcActionButton v-if="!isCurrent" :close-after-click="true" @click="restoreVersion"> @@ -116,6 +124,7 @@ <script> import BackupRestore from 'vue-material-design-icons/BackupRestore.vue' import Download from 'vue-material-design-icons/Download.vue' +import FileCompare from 'vue-material-design-icons/FileCompare.vue' import Pencil from 'vue-material-design-icons/Pencil.vue' import Check from 'vue-material-design-icons/Check.vue' import Delete from 'vue-material-design-icons/Delete.vue' @@ -124,7 +133,7 @@ import { NcActionButton, NcActionLink, NcListItem, NcModal, NcButton, NcTextFiel import moment from '@nextcloud/moment' import { translate } from '@nextcloud/l10n' import { joinPaths } from '@nextcloud/paths' -import { generateUrl, getRootUrl } from '@nextcloud/router' +import { getRootUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' export default { @@ -138,6 +147,7 @@ export default { NcTextField, BackupRestore, Download, + FileCompare, Pencil, Check, Delete, @@ -184,6 +194,14 @@ export default { type: Boolean, default: false, }, + canView: { + type: Boolean, + default: false, + }, + canCompare: { + type: Boolean, + default: false, + }, }, data() { return { @@ -226,20 +244,6 @@ export default { } }, - /** - * @return {string} - */ - previewURL() { - if (this.isCurrent) { - return generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0', { - fileId: this.fileInfo.id, - fileEtag: this.fileInfo.etag, - }) - } else { - return this.version.preview - } - }, - /** @return {string} */ formattedDate() { return moment(this.version.mtime).format('LLL') @@ -253,7 +257,7 @@ export default { /** @return {boolean} */ enableDeletion() { return this.capabilities.files.version_deletion === true && this.fileInfo.mountType !== 'group' - } + }, }, methods: { openVersionLabelModal() { @@ -276,6 +280,21 @@ export default { deleteVersion() { this.$emit('delete', this.version) }, + + click() { + if (!this.canView) { + window.location = this.downloadURL + return + } + this.$emit('click', { version: this.version }) + }, + + compareVersion() { + if (!this.canView) { + throw new Error('Cannot compare version of this file') + } + this.$emit('compare', { version: this.version }) + }, }, } </script> diff --git a/apps/files_versions/src/utils/davRequest.js b/apps/files_versions/src/utils/davRequest.js index fec64caa2d5..d3fe729aaab 100644 --- a/apps/files_versions/src/utils/davRequest.js +++ b/apps/files_versions/src/utils/davRequest.js @@ -29,6 +29,7 @@ export default `<?xml version="1.0"?> <d:getcontentlength /> <d:getcontenttype /> <d:getlastmodified /> + <d:getetag /> <nc:version-label /> <nc:has-preview /> </d:prop> diff --git a/apps/files_versions/src/utils/versions.js b/apps/files_versions/src/utils/versions.js index 71593dd0ce8..7f5ccfc0e69 100644 --- a/apps/files_versions/src/utils/versions.js +++ b/apps/files_versions/src/utils/versions.js @@ -20,25 +20,33 @@ */ import { getCurrentUser } from '@nextcloud/auth' +import { joinPaths } from '@nextcloud/paths' +import { generateRemoteUrl, generateUrl } from '@nextcloud/router' +import moment from '@nextcloud/moment' + +import { encodeFilePath } from '../../../files/src/utils/fileUtils.js' + import client from '../utils/davClient.js' import davRequest from '../utils/davRequest.js' import logger from '../utils/logger.js' -import { joinPaths } from '@nextcloud/paths' -import { generateUrl } from '@nextcloud/router' -import moment from '@nextcloud/moment' +import path from 'path' /** * @typedef {object} Version * @property {string} fileId - The id of the file associated to the version. * @property {string} label - 'Current version' or '' - * @property {string} fileName - File name relative to the version DAV endpoint - * @property {string} mimeType - Empty for the current version, else the actual mime type of the version + * @property {string} filename - File name relative to the version DAV endpoint + * @property {string} basename - A base name generated from the mtime + * @property {string} mime - Empty for the current version, else the actual mime type of the version + * @property {string} etag - Empty for the current version, else the actual mime type of the version * @property {string} size - Human readable size * @property {string} type - 'file' * @property {number} mtime - Version creation date as a timestamp + * @property {string} permissions - Only readable: 'R' * @property {boolean} hasPreview - Whether the version has a preview - * @property {string} preview - Preview URL of the version + * @property {string} previewUrl - Preview URL of the version * @property {string} url - Download URL of the version + * @property {string} source - The WebDAV endpoint of the ressource * @property {string|null} fileVersion - The version id, null for the current version */ @@ -75,7 +83,7 @@ export async function restoreVersion(version) { logger.debug('Restoring version', { url: version.url }) await client.moveFile( `/versions/${getCurrentUser()?.uid}/versions/${version.fileId}/${version.fileVersion}`, - `/versions/${getCurrentUser()?.uid}/restore/target` + `/versions/${getCurrentUser()?.uid}/restore/target`, ) } catch (exception) { logger.error('Could not restore version', { exception }) @@ -91,20 +99,39 @@ export async function restoreVersion(version) { * @return {Version} */ function formatVersion(version, fileInfo) { + const mtime = moment(version.lastmod).unix() * 1000 + let previewUrl = '' + let filename = '' + + if (mtime === fileInfo.mtime) { // Version is the current one + filename = path.join('files', getCurrentUser()?.uid ?? '', fileInfo.path, fileInfo.name) + previewUrl = generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0', { + fileId: fileInfo.id, + fileEtag: fileInfo.etag, + }) + } else { + filename = version.filename + previewUrl = generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', { + file: joinPaths(fileInfo.path, fileInfo.name), + fileVersion: version.basename, + }) + } + return { fileId: fileInfo.id, label: version.props['version-label'], - fileName: version.filename, - mimeType: version.mime, + filename, + basename: moment(mtime).format('LLL'), + mime: version.mime, + etag: `${version.props.getetag}`, size: version.size, type: version.type, - mtime: moment(version.lastmod).unix() * 1000, + mtime, + permissions: 'R', hasPreview: version.props['has-preview'] === 1, - preview: generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', { - file: joinPaths(fileInfo.path, fileInfo.name), - fileVersion: version.basename, - }), - url: joinPaths('/remote.php/dav', version.filename), + previewUrl, + url: joinPaths('/remote.php/dav', filename), + source: generateRemoteUrl('dav') + encodeFilePath(filename), fileVersion: version.basename, } } @@ -115,7 +142,7 @@ function formatVersion(version, fileInfo) { */ export async function setVersionLabel(version, newLabel) { return await client.customRequest( - version.fileName, + version.filename, { method: 'PROPPATCH', data: `<?xml version="1.0"?> @@ -129,7 +156,7 @@ export async function setVersionLabel(version, newLabel) { </d:prop> </d:set> </d:propertyupdate>`, - } + }, ) } @@ -137,5 +164,5 @@ export async function setVersionLabel(version, newLabel) { * @param {Version} version */ export async function deleteVersion(version) { - await client.deleteFile(version.fileName) + await client.deleteFile(version.filename) } diff --git a/apps/files_versions/src/views/VersionTab.vue b/apps/files_versions/src/views/VersionTab.vue index 6d3b07c2f88..a8b7ce10092 100644 --- a/apps/files_versions/src/views/VersionTab.vue +++ b/apps/files_versions/src/views/VersionTab.vue @@ -19,11 +19,15 @@ <ul data-files-versions-versions-list> <Version v-for="version in orderedVersions" :key="version.mtime" + :can-view="canView" + :can-compare="canCompare" :load-preview="isActive" :version="version" :file-info="fileInfo" :is-current="version.mtime === fileInfo.mtime" :is-first-version="version.mtime === initialVersionMtime" + @click="openVersion" + @compare="compareVersion" @restore="handleRestore" @label-update="handleLabelUpdate" @delete="handleDelete" /> @@ -32,6 +36,7 @@ <script> import { showError, showSuccess } from '@nextcloud/dialogs' +import isMobile from '@nextcloud/vue/dist/Mixins/isMobile.js' import { fetchVersions, deleteVersion, restoreVersion, setVersionLabel } from '../utils/versions.js' import Version from '../components/Version.vue' @@ -40,6 +45,9 @@ export default { components: { Version, }, + mixins: [ + isMobile, + ], data() { return { fileInfo: null, @@ -78,6 +86,37 @@ export default { .map(version => version.mtime) .reduce((a, b) => Math.min(a, b)) }, + + viewerFileInfo() { + // We need to remap bitmask to dav permissions as the file info we have is converted through client.js + let davPermissions = '' + if (this.fileInfo.permissions & 1) { + davPermissions += 'R' + } + if (this.fileInfo.permissions & 2) { + davPermissions += 'W' + } + if (this.fileInfo.permissions & 8) { + davPermissions += 'D' + } + return { + ...this.fileInfo, + mime: this.fileInfo.mimetype, + basename: this.fileInfo.name, + filename: this.fileInfo.path + this.fileInfo.name, + permissions: davPermissions, + fileid: this.fileInfo.id, + } + }, + + /** @return {boolean} */ + canView() { + return window.OCA.Viewer?.mimetypesCompare?.includes(this.fileInfo.mimetype) + }, + + canCompare() { + return !this.isMobile + }, }, methods: { /** @@ -182,6 +221,29 @@ export default { resetState() { this.$set(this, 'versions', []) }, + + openVersion({ version }) { + // Open current file view instead of read only + if (version.mtime === this.fileInfo.mtime) { + OCA.Viewer.open({ fileInfo: this.viewerFileInfo }) + return + } + + // Versions previews are too small for our use case, so we override hasPreview and previewUrl + // which makes the viewer render the original file. + const versions = this.versions.map(version => ({ ...version, hasPreview: false, previewUrl: undefined })) + + OCA.Viewer.open({ + fileInfo: versions.find(v => v.source === version.source), + enableSidebar: false, + }) + }, + + compareVersion({ version }) { + const versions = this.versions.map(version => ({ ...version, hasPreview: false, previewUrl: undefined })) + + OCA.Viewer.compare(this.viewerFileInfo, versions.find(v => v.source === version.source)) + }, }, } </script> |