Skip to content

Commit

Permalink
Merge pull request #39763 from nextcloud/backport/39171/stable27
Browse files Browse the repository at this point in the history
  • Loading branch information
juliushaertl committed Aug 9, 2023
2 parents b2589c8 + 12bea04 commit b86e0f6
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 44 deletions.
59 changes: 39 additions & 20 deletions apps/files_versions/src/components/Version.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -46,14 +46,22 @@
</div>
</template>
<template #actions>
<NcActionButton v-if="enableLabeling"
<NcActionButton v-if="enableLabeling"
:close-after-click="true"
@click="openVersionLabelModal">
<template #icon>
<Pencil :size="22" />
</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">
Expand Down Expand Up @@ -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'
Expand All @@ -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 {
Expand All @@ -138,6 +147,7 @@ export default {
NcTextField,
BackupRestore,
Download,
FileCompare,
Pencil,
Check,
Delete,
Expand Down Expand Up @@ -184,6 +194,14 @@ export default {
type: Boolean,
default: false,
},
canView: {
type: Boolean,
default: false,
},
canCompare: {
type: Boolean,
default: false,
},
},
data() {
return {
Expand Down Expand Up @@ -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')
Expand All @@ -253,7 +257,7 @@ export default {
/** @return {boolean} */
enableDeletion() {
return this.capabilities.files.version_deletion === true && this.fileInfo.mountType !== 'group'
}
},
},
methods: {
openVersionLabelModal() {
Expand All @@ -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>
Expand Down
1 change: 1 addition & 0 deletions apps/files_versions/src/utils/davRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
63 changes: 45 additions & 18 deletions apps/files_versions/src/utils/versions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

Expand Down Expand Up @@ -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 })
Expand All @@ -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,
}
}
Expand All @@ -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"?>
Expand All @@ -129,13 +156,13 @@ export async function setVersionLabel(version, newLabel) {
</d:prop>
</d:set>
</d:propertyupdate>`,
}
},
)
}

/**
* @param {Version} version
*/
export async function deleteVersion(version) {
await client.deleteFile(version.fileName)
await client.deleteFile(version.filename)
}
62 changes: 62 additions & 0 deletions apps/files_versions/src/views/VersionTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand All @@ -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'
Expand All @@ -40,6 +45,9 @@ export default {
components: {
Version,
},
mixins: [
isMobile,
],
data() {
return {
fileInfo: null,
Expand Down Expand Up @@ -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: {
/**
Expand Down Expand Up @@ -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>
4 changes: 2 additions & 2 deletions dist/core-common.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/core-common.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/files_versions-files_versions.js

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions dist/files_versions-files_versions.js.LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,25 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* @copyright Copyright (c) 2021 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
2 changes: 1 addition & 1 deletion dist/files_versions-files_versions.js.map

Large diffs are not rendered by default.

0 comments on commit b86e0f6

Please sign in to comment.