|
|
@@ -17,58 +17,78 @@ |
|
|
|
--> |
|
|
|
<template> |
|
|
|
<div> |
|
|
|
<ul> |
|
|
|
<li v-for="version in versions" class="version"> |
|
|
|
<NcListItem v-for="version in versions" |
|
|
|
class="version" |
|
|
|
key="version.url" |
|
|
|
:title="version.title" |
|
|
|
:href="version.url"> |
|
|
|
<template #icon> |
|
|
|
<img lazy="true" |
|
|
|
:src="version.preview" |
|
|
|
alt="" |
|
|
|
height="256" |
|
|
|
width="256" |
|
|
|
class="version-image"> |
|
|
|
</template> |
|
|
|
<template #subtitle> |
|
|
|
<div class="version-info"> |
|
|
|
<a v-tooltip="version.dateTime" :href="version.url">{{ version.relativeTime }}</a> |
|
|
|
<div class="version-info-size"> |
|
|
|
<span class="version-info-size">•</span> |
|
|
|
<span class="version-info-size"> |
|
|
|
{{ version.size }} |
|
|
|
</div> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
<NcButton v-tooltip="t('files_versions', `Download file ${fileInfo.name} with version ${version.displayVersionName}`)" |
|
|
|
type="secondary" |
|
|
|
class="download-button" |
|
|
|
:href="version.url" |
|
|
|
:aria-label="t('files_versions', `Download file ${fileInfo.name} with version ${version.displayVersionName}`)"> |
|
|
|
</template> |
|
|
|
<template #actions> |
|
|
|
<NcActionLink :href="version.url"> |
|
|
|
<template #icon> |
|
|
|
<Download :size="22" /> |
|
|
|
</template> |
|
|
|
</NcButton> |
|
|
|
<NcButton v-tooltip="t('files_versions', `Restore file ${fileInfo.name} with version ${version.displayVersionName}`)" |
|
|
|
type="secondary" |
|
|
|
class="restore-button" |
|
|
|
:aria-label="t('files_versions', `Restore file ${fileInfo.name} with version ${version.displayVersionName}`)" |
|
|
|
@click="restoreVersion(version)"> |
|
|
|
{{ t('files_versions', `Download file ${fileInfo.name} with version ${version.displayVersionName}`) }} |
|
|
|
</NcActionLink> |
|
|
|
<NcActionButton @click="restoreVersion(version)" v-if="!version.isCurrent"> |
|
|
|
<template #icon> |
|
|
|
<BackupRestore :size="22" /> |
|
|
|
</template> |
|
|
|
</NcButton> |
|
|
|
</li> |
|
|
|
</ul> |
|
|
|
{{ t('files_versions', `Restore file ${fileInfo.name} with version ${version.displayVersionName}`) }} |
|
|
|
</NcActionButton> |
|
|
|
</template> |
|
|
|
</NcListItem> |
|
|
|
<NcEmptyContent v-if="!loading && versions.length === 1" |
|
|
|
:title="t('files_version', 'No versions yet')"> |
|
|
|
<!-- length === 1, since we don't want to show versions if there is only the current file --> |
|
|
|
<template #icon> |
|
|
|
<BackupRestore /> |
|
|
|
</template> |
|
|
|
</NcEmptyContent> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { createClient, getPatcher } from 'webdav' |
|
|
|
import axios from '@nextcloud/axios' |
|
|
|
import parseUrl from 'url-parse' |
|
|
|
import { generateRemoteUrl, generateUrl } from '@nextcloud/router' |
|
|
|
import { getCurrentUser } from '@nextcloud/auth' |
|
|
|
import { translate } from '@nextcloud/l10n' |
|
|
|
import BackupRestore from 'vue-material-design-icons/BackupRestore.vue' |
|
|
|
import Download from 'vue-material-design-icons/Download.vue' |
|
|
|
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' |
|
|
|
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' |
|
|
|
import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js' |
|
|
|
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js' |
|
|
|
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' |
|
|
|
import { showError, showSuccess } from '@nextcloud/dialogs' |
|
|
|
import moment from '@nextcloud/moment' |
|
|
|
import { basename, joinPaths } from '@nextcloud/paths' |
|
|
|
import { getLoggerBuilder } from '@nextcloud/logger' |
|
|
|
import { translate } from '@nextcloud/l10n' |
|
|
|
|
|
|
|
const logger = getLoggerBuilder() |
|
|
|
.setApp('files_version') |
|
|
|
.detectUser() |
|
|
|
.build() |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* Get WebDAV request body for version list |
|
|
|
*/ |
|
|
|
function getDavRequest() { |
|
|
|
return `<?xml version="1.0"?> |
|
|
@@ -85,55 +105,64 @@ function getDavRequest() { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* @param version |
|
|
|
* @param fileInfo |
|
|
|
* Format version |
|
|
|
*/ |
|
|
|
function formatVersion(version, fileInfo) { |
|
|
|
const fileVersion = basename(version.filename) |
|
|
|
const isCurrent = version.mime === '' |
|
|
|
|
|
|
|
const preview = generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', { |
|
|
|
file: joinPaths(fileInfo.path, fileInfo.name), |
|
|
|
fileVersion, |
|
|
|
}) |
|
|
|
const preview = isCurrent |
|
|
|
? generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0', { |
|
|
|
fileId: fileInfo.id, |
|
|
|
fileEtag: fileInfo.etag, |
|
|
|
}) : generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', { |
|
|
|
file: joinPaths(fileInfo.path, fileInfo.name), |
|
|
|
fileVersion, |
|
|
|
}) |
|
|
|
|
|
|
|
return { |
|
|
|
displayVersionName: fileVersion, |
|
|
|
title: isCurrent ? translate('files_versions', 'Current version') : '', |
|
|
|
fileName: version.filename, |
|
|
|
mimeType: version.mime, |
|
|
|
size: OC.Util.humanFileSize(version.size), |
|
|
|
size: OC.Util.humanFileSize(isCurrent ? fileInfo.size : version.size), |
|
|
|
type: version.type, |
|
|
|
dateTime: moment(version.lastmod), |
|
|
|
relativeTime: moment(version.lastmod).fromNow(), |
|
|
|
dateTime: moment(isCurrent ? fileInfo.mtime : version.lastmod), |
|
|
|
relativeTime: moment(isCurrent ? fileInfo.mtime : version.lastmod).fromNow(), |
|
|
|
preview, |
|
|
|
url: joinPaths('/remote.php/dav', version.filename), |
|
|
|
url: isCurrent ? joinPaths('/remote.php/dav', version.filename) : joinPaths('/remote.php/dav', fileInfo.path, fileInfo.name), |
|
|
|
fileVersion, |
|
|
|
isCurrent, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const rootPath = 'dav' |
|
|
|
|
|
|
|
// force our axios |
|
|
|
const patcher = getPatcher() |
|
|
|
patcher.patch('request', axios) |
|
|
|
|
|
|
|
// init webdav client on default dav endpoint |
|
|
|
const remote = generateRemoteUrl(rootPath) |
|
|
|
const client = createClient(remote) |
|
|
|
|
|
|
|
export default { |
|
|
|
name: 'VersionTab', |
|
|
|
components: { |
|
|
|
NcButton, |
|
|
|
NcEmptyContent, |
|
|
|
NcActionLink, |
|
|
|
NcActionButton, |
|
|
|
NcListItem, |
|
|
|
BackupRestore, |
|
|
|
Download, |
|
|
|
}, |
|
|
|
data() { |
|
|
|
const rootPath = 'dav' |
|
|
|
|
|
|
|
// force our axios |
|
|
|
const patcher = getPatcher() |
|
|
|
patcher.patch('request', axios) |
|
|
|
|
|
|
|
// init webdav client on default dav endpoint |
|
|
|
const remote = generateRemoteUrl(rootPath) |
|
|
|
const client = createClient(remote) |
|
|
|
|
|
|
|
return { |
|
|
|
fileInfo: null, |
|
|
|
versions: [], |
|
|
|
client, |
|
|
|
remote, |
|
|
|
loading: true, |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
@@ -153,13 +182,17 @@ export default { |
|
|
|
*/ |
|
|
|
async fetchVersions() { |
|
|
|
const path = `/versions/${getCurrentUser().uid}/versions/${this.fileInfo.id}` |
|
|
|
const remotePath = parseUrl(this.remote).pathname |
|
|
|
|
|
|
|
const response = await this.client.getDirectoryContents(path, { |
|
|
|
data: getDavRequest(), |
|
|
|
}) |
|
|
|
this.versions = response.filter(version => version.mime !== '') |
|
|
|
.map(version => formatVersion(version, this.fileInfo)) |
|
|
|
try { |
|
|
|
const response = await client.getDirectoryContents(path, { |
|
|
|
data: getDavRequest(), |
|
|
|
}) |
|
|
|
this.versions = response.map(version => formatVersion(version, this.fileInfo)) |
|
|
|
this.loading = false |
|
|
|
} catch (exception) { |
|
|
|
logger.error('Could not fetch version', {exception}) |
|
|
|
this.loading = false |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
@@ -169,15 +202,15 @@ export default { |
|
|
|
*/ |
|
|
|
async restoreVersion(version) { |
|
|
|
try { |
|
|
|
console.debug('restore version', version.url) |
|
|
|
const response = await this.client.moveFile( |
|
|
|
logger.debug('restoring version', version.url) |
|
|
|
const response = await client.moveFile( |
|
|
|
`/versions/${getCurrentUser().uid}/versions/${this.fileInfo.id}/${version.fileVersion}`, |
|
|
|
`/versions/${getCurrentUser().uid}/restore/target` |
|
|
|
) |
|
|
|
showSuccess(t('files_versions', 'Version restored')) |
|
|
|
await this.fetchVersions() |
|
|
|
} catch (exception) { |
|
|
|
console.error('Could not restore version', exception) |
|
|
|
logger.error('Could not restore version', {exception}) |
|
|
|
showError(t('files_versions', 'Could not restore version')) |
|
|
|
} |
|
|
|
}, |
|
|
@@ -198,7 +231,9 @@ export default { |
|
|
|
flex-direction: row; |
|
|
|
&-info { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
flex-direction: row; |
|
|
|
align-items: center; |
|
|
|
gap: 0.5rem; |
|
|
|
&-size { |
|
|
|
color: var(--color-text-lighter); |
|
|
|
} |
|
|
@@ -206,17 +241,9 @@ export default { |
|
|
|
&-image { |
|
|
|
width: 3rem; |
|
|
|
height: 3rem; |
|
|
|
filter: drop-shadow(0 1px 2px var(--color-box-shadow)); |
|
|
|
border: 1px solid var(--color-border); |
|
|
|
margin-right: 1rem; |
|
|
|
border-radius: var(--border-radius); |
|
|
|
} |
|
|
|
.restore-button { |
|
|
|
margin-left: 1rem; |
|
|
|
align-self: center; |
|
|
|
} |
|
|
|
.download-button { |
|
|
|
margin-left: auto; |
|
|
|
align-self: center; |
|
|
|
border-radius: var(--border-radius-large); |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |