aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_versions/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_versions/src')
-rw-r--r--apps/files_versions/src/components/Version.vue166
-rw-r--r--apps/files_versions/src/components/VersionLabelDialog.vue123
-rw-r--r--apps/files_versions/src/components/VersionLabelForm.vue115
-rw-r--r--apps/files_versions/src/components/VirtualScrolling.vue23
-rw-r--r--apps/files_versions/src/css/versions.css17
-rw-r--r--apps/files_versions/src/files_versions_tab.js22
-rw-r--r--apps/files_versions/src/utils/davClient.js34
-rw-r--r--apps/files_versions/src/utils/davRequest.js21
-rw-r--r--apps/files_versions/src/utils/logger.js20
-rw-r--r--apps/files_versions/src/utils/versions.ts29
-rw-r--r--apps/files_versions/src/views/VersionTab.vue108
11 files changed, 300 insertions, 378 deletions
diff --git a/apps/files_versions/src/components/Version.vue b/apps/files_versions/src/components/Version.vue
index 1afe28250c6..dc36e4134f9 100644
--- a/apps/files_versions/src/components/Version.vue
+++ b/apps/files_versions/src/components/Version.vue
@@ -1,29 +1,17 @@
<!--
- - @copyright 2022 Carl Schwan <carl@carlschwan.eu>
- - @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/>.
- -->
+ - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<NcListItem class="version"
:force-display-actions="true"
+ :actions-aria-label="t('files_versions', 'Actions for version from {versionHumanExplicitDate}', { versionHumanExplicitDate })"
:data-files-versions-version="version.fileVersion"
@click="click">
<!-- Icon -->
<template #icon>
<div v-if="!(loadPreview || previewLoaded)" class="version__image" />
- <img v-else-if="(isCurrent || version.hasPreview) && !previewErrored"
+ <img v-else-if="version.previewUrl && !previewErrored"
:src="version.previewUrl"
alt=""
decoding="async"
@@ -41,16 +29,26 @@
<!-- author -->
<template #name>
<div class="version__info">
- <div v-if="versionLabel" class="version__info__label">{{ versionLabel }}</div>
- <div v-if="versionAuthor" class="version__info">
- <div v-if="versionLabel">•</div>
+ <div v-if="versionLabel"
+ class="version__info__label"
+ data-cy-files-version-label
+ :title="versionLabel">
+ {{ versionLabel }}
+ </div>
+ <div v-if="versionAuthor"
+ class="version__info"
+ data-cy-files-version-author-name>
+ <span v-if="versionLabel">•</span>
<NcAvatar class="avatar"
:user="version.author"
- :size="16"
- :disable-menu="true"
- :disable-tooltip="true"
+ :size="20"
+ disable-menu
+ disable-tooltip
:show-user-status="false" />
- <div>{{ versionAuthor }}</div>
+ <div class="version__info__author_name"
+ :title="versionAuthor">
+ {{ versionAuthor }}
+ </div>
</div>
</div>
</template>
@@ -58,10 +56,12 @@
<!-- Version file size as subline -->
<template #subname>
<div class="version__info version__info__subline">
- <span :title="formattedDate">{{ version.mtime | humanDateFromNow }}</span>
- <!-- Separate dot to improve alignement -->
+ <NcDateTime class="version__info__date"
+ relative-time="short"
+ :timestamp="version.mtime" />
+ <!-- Separate dot to improve alignment -->
<span>•</span>
- <span>{{ version.size | humanReadableSize }}</span>
+ <span>{{ humanReadableSize }}</span>
</div>
</template>
@@ -116,31 +116,35 @@
</template>
</NcListItem>
</template>
-
<script lang="ts">
+import type { PropType } from 'vue'
import type { Version } from '../utils/versions'
+import { getCurrentUser } from '@nextcloud/auth'
+import { Permission, formatFileSize } from '@nextcloud/files'
+import { loadState } from '@nextcloud/initial-state'
+import { t } from '@nextcloud/l10n'
+import { joinPaths } from '@nextcloud/paths'
+import { getRootUrl, generateUrl } from '@nextcloud/router'
+import { defineComponent } from 'vue'
+
+import axios from '@nextcloud/axios'
+import moment from '@nextcloud/moment'
+import logger from '../utils/logger'
+
import BackupRestore from 'vue-material-design-icons/BackupRestore.vue'
import Delete from 'vue-material-design-icons/Delete.vue'
import Download from 'vue-material-design-icons/Download.vue'
import FileCompare from 'vue-material-design-icons/FileCompare.vue'
import ImageOffOutline from 'vue-material-design-icons/ImageOffOutline.vue'
-import Pencil from 'vue-material-design-icons/Pencil.vue'
+import Pencil from 'vue-material-design-icons/PencilOutline.vue'
-import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
-import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js'
-import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
-import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
-import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'
-
-import { defineComponent, type PropType } from 'vue'
-import axios from '@nextcloud/axios'
-import { getRootUrl, generateOcsUrl } from '@nextcloud/router'
-import { joinPaths } from '@nextcloud/paths'
-import { loadState } from '@nextcloud/initial-state'
-import { Permission, formatFileSize } from '@nextcloud/files'
-import { translate as t } from '@nextcloud/l10n'
-import moment from '@nextcloud/moment'
+import NcActionButton from '@nextcloud/vue/components/NcActionButton'
+import NcActionLink from '@nextcloud/vue/components/NcActionLink'
+import NcAvatar from '@nextcloud/vue/components/NcAvatar'
+import NcDateTime from '@nextcloud/vue/components/NcDateTime'
+import NcListItem from '@nextcloud/vue/components/NcListItem'
+import Tooltip from '@nextcloud/vue/directives/Tooltip'
const hasPermission = (permissions: number, permission: number): boolean => (permissions & permission) !== 0
@@ -151,6 +155,7 @@ export default defineComponent({
NcActionLink,
NcActionButton,
NcAvatar,
+ NcDateTime,
NcListItem,
BackupRestore,
Download,
@@ -164,20 +169,6 @@ export default defineComponent({
tooltip: Tooltip,
},
- created() {
- this.fetchDisplayName()
- },
-
- filters: {
- humanReadableSize(bytes: number): string {
- return formatFileSize(bytes)
- },
-
- humanDateFromNow(timestamp: number): string {
- return moment(timestamp).fromNow()
- },
- },
-
props: {
version: {
type: Object as PropType<Version>,
@@ -216,11 +207,15 @@ export default defineComponent({
previewLoaded: false,
previewErrored: false,
capabilities: loadState('core', 'capabilities', { files: { version_labeling: false, version_deletion: false } }),
- versionAuthor: '',
+ versionAuthor: '' as string | null,
}
},
computed: {
+ humanReadableSize() {
+ return formatFileSize(this.version.size)
+ },
+
versionLabel(): string {
const label = this.version.label ?? ''
@@ -239,6 +234,10 @@ export default defineComponent({
return label
},
+ versionHumanExplicitDate(): string {
+ return moment(this.version.mtime).format('LLLL')
+ },
+
downloadURL(): string {
if (this.isCurrent) {
return getRootUrl() + joinPaths('/remote.php/webdav', this.fileInfo.path, this.fileInfo.name)
@@ -247,10 +246,6 @@ export default defineComponent({
}
},
- formattedDate(): string {
- return moment(this.version.mtime).format('LLL')
- },
-
enableLabeling(): boolean {
return this.capabilities.files.version_labeling === true
},
@@ -277,7 +272,7 @@ export default defineComponent({
const downloadAttribute = this.fileInfo.shareAttributes
.find((attribute) => attribute.scope === 'permissions' && attribute.key === 'download') || {}
// If the download attribute is set to false, the file is not downloadable
- if (downloadAttribute?.enabled === false) {
+ if (downloadAttribute?.value === false) {
return false
}
}
@@ -286,6 +281,10 @@ export default defineComponent({
},
},
+ created() {
+ this.fetchDisplayName()
+ },
+
methods: {
labelUpdate() {
this.$emit('label-update-request')
@@ -304,21 +303,26 @@ export default defineComponent({
},
async fetchDisplayName() {
- // check to make sure that we have a valid author - in case database did not migrate, null author, etc.
- if (this.version.author) {
+ this.versionAuthor = null
+ if (!this.version.author) {
+ return
+ }
+
+ if (this.version.author === getCurrentUser()?.uid) {
+ this.versionAuthor = t('files_versions', 'You')
+ } else {
try {
- const { data } = await axios.get(generateOcsUrl(`/cloud/users/${this.version.author}`))
- this.versionAuthor = data.ocs.data.displayname
- } catch (e) {
- // Promise got rejected - default to null author to not try to load author profile
- this.versionAuthor = null
+ const { data } = await axios.post(generateUrl('/displaynames'), { users: [this.version.author] })
+ this.versionAuthor = data.users[this.version.author]
+ } catch (error) {
+ logger.warn('Could not load user display name', { error })
}
}
},
click() {
if (!this.canView) {
- window.location = this.downloadURL
+ window.location.href = this.downloadURL
return
}
this.$emit('click', { version: this.version })
@@ -348,14 +352,30 @@ export default defineComponent({
gap: 0.5rem;
color: var(--color-main-text);
font-weight: 500;
+ overflow: hidden;
&__label {
font-weight: 700;
+ // Fix overflow on narrow screens
+ overflow: hidden;
+ text-overflow: ellipsis;
+ min-width: 110px;
+ }
+
+ &__author_name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ &__date {
+ // Fix overflow on narrow screens
+ overflow: hidden;
+ text-overflow: ellipsis;
}
&__subline {
- color: var(--color-text-maxcontrast)
- }
+ color: var(--color-text-maxcontrast)
+ }
}
&__image {
diff --git a/apps/files_versions/src/components/VersionLabelDialog.vue b/apps/files_versions/src/components/VersionLabelDialog.vue
new file mode 100644
index 00000000000..760780cae61
--- /dev/null
+++ b/apps/files_versions/src/components/VersionLabelDialog.vue
@@ -0,0 +1,123 @@
+<!--
+ - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <NcDialog :buttons="dialogButtons"
+ content-classes="version-label-modal"
+ is-form
+ :open="open"
+ size="normal"
+ :name="t('files_versions', 'Name this version')"
+ @update:open="$emit('update:open', $event)"
+ @submit="setVersionLabel(editedVersionLabel)">
+ <NcTextField ref="labelInput"
+ class="version-label-modal__input"
+ :label="t('files_versions', 'Version name')"
+ :placeholder="t('files_versions', 'Version name')"
+ :value.sync="editedVersionLabel" />
+
+ <p class="version-label-modal__info">
+ {{ t('files_versions', 'Named versions are persisted, and excluded from automatic cleanups when your storage quota is full.') }}
+ </p>
+ </NcDialog>
+</template>
+
+<script lang="ts">
+import { t } from '@nextcloud/l10n'
+import { defineComponent } from 'vue'
+import svgCheck from '@mdi/svg/svg/check.svg?raw'
+
+import NcDialog from '@nextcloud/vue/components/NcDialog'
+import NcTextField from '@nextcloud/vue/components/NcTextField'
+
+type Focusable = Vue & { focus: () => void }
+
+export default defineComponent({
+ name: 'VersionLabelDialog',
+ components: {
+ NcDialog,
+ NcTextField,
+ },
+ props: {
+ open: {
+ type: Boolean,
+ default: false,
+ },
+ versionLabel: {
+ type: String,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ editedVersionLabel: '',
+ }
+ },
+ computed: {
+ dialogButtons() {
+ const buttons: unknown[] = []
+ if (this.versionLabel.trim() === '') {
+ // If there is no label just offer a cancel action that just closes the dialog
+ buttons.push({
+ label: t('files_versions', 'Cancel'),
+ })
+ } else {
+ // If there is already a label set, offer to remove the version label
+ buttons.push({
+ label: t('files_versions', 'Remove version name'),
+ type: 'error',
+ nativeType: 'reset',
+ callback: () => { this.setVersionLabel('') },
+ })
+ }
+ return [
+ ...buttons,
+ {
+ label: t('files_versions', 'Save version name'),
+ type: 'primary',
+ nativeType: 'submit',
+ icon: svgCheck,
+ },
+ ]
+ },
+ },
+ watch: {
+ versionLabel: {
+ immediate: true,
+ handler(label) {
+ this.editedVersionLabel = label ?? ''
+ },
+ },
+ open: {
+ immediate: true,
+ handler(open) {
+ if (open) {
+ this.$nextTick(() => (this.$refs.labelInput as Focusable).focus())
+ }
+ this.editedVersionLabel = this.versionLabel
+ },
+ },
+ },
+ methods: {
+ setVersionLabel(label: string) {
+ this.$emit('label-update', label)
+ },
+
+ t,
+ },
+})
+</script>
+
+<style scoped lang="scss">
+.version-label-modal {
+ &__info {
+ color: var(--color-text-maxcontrast);
+ margin-block: calc(3 * var(--default-grid-baseline));
+ }
+
+ &__input {
+ margin-block-start: calc(2 * var(--default-grid-baseline));
+ }
+}
+</style>
diff --git a/apps/files_versions/src/components/VersionLabelForm.vue b/apps/files_versions/src/components/VersionLabelForm.vue
deleted file mode 100644
index 5b8251b36c0..00000000000
--- a/apps/files_versions/src/components/VersionLabelForm.vue
+++ /dev/null
@@ -1,115 +0,0 @@
-<!--
- - @copyright Copyright (c) 2024 Louis Chemineau <louis@chmn.me>
- -
- - @author Louis Chemineau <louis@chmn.me>
- -
- - @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/>.
- -->
-<template>
- <form class="version-label-modal"
- @submit.prevent="setVersionLabel(innerVersionLabel)">
- <label>
- <div class="version-label-modal__title">{{ t('files_versions', 'Version name') }}</div>
- <NcTextField ref="labelInput"
- :value.sync="innerVersionLabel"
- :placeholder="t('files_versions', 'Version name')"
- :label-outside="true" />
- </label>
-
- <div class="version-label-modal__info">
- {{ t('files_versions', 'Named versions are persisted, and excluded from automatic cleanups when your storage quota is full.') }}
- </div>
-
- <div class="version-label-modal__actions">
- <NcButton :disabled="innerVersionLabel.trim().length === 0" @click="setVersionLabel('')">
- {{ t('files_versions', 'Remove version name') }}
- </NcButton>
- <NcButton type="primary" native-type="submit">
- <template #icon>
- <Check />
- </template>
- {{ t('files_versions', 'Save version name') }}
- </NcButton>
- </div>
- </form>
-</template>
-
-<script lang="ts">
-import Check from 'vue-material-design-icons/Check.vue'
-import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
-import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
-import { translate } from '@nextcloud/l10n'
-
-import { defineComponent } from 'vue'
-
-export default defineComponent({
- name: 'VersionLabelForm',
- components: {
- NcButton,
- NcTextField,
- Check,
- },
- props: {
- versionLabel: {
- type: String,
- default: '',
- },
- },
- data() {
- return {
- innerVersionLabel: this.versionLabel,
- }
- },
- mounted() {
- this.$nextTick(() => {
- (this.$refs.labelInput as Vue).$el.getElementsByTagName('input')[0].focus()
- })
- },
- methods: {
- setVersionLabel(label: string) {
- this.$emit('label-update', label)
- },
-
- t: translate,
- },
-})
-</script>
-
-<style scoped lang="scss">
-.version-label-modal {
- display: flex;
- justify-content: space-between;
- flex-direction: column;
- height: 250px;
- padding: 16px;
-
- &__title {
- margin-bottom: 12px;
- font-weight: 600;
- }
-
- &__info {
- margin-top: 12px;
- color: var(--color-text-maxcontrast);
- }
-
- &__actions {
- display: flex;
- justify-content: space-between;
- margin-top: 64px;
- }
-}
-</style>
diff --git a/apps/files_versions/src/components/VirtualScrolling.vue b/apps/files_versions/src/components/VirtualScrolling.vue
index fc199edd2ef..5a502036839 100644
--- a/apps/files_versions/src/components/VirtualScrolling.vue
+++ b/apps/files_versions/src/components/VirtualScrolling.vue
@@ -1,24 +1,7 @@
<!--
- - @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
- -
- - @author Louis Chemineau <louis@chmn.me>
- -
- - @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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<div v-if="!useWindow && containerElement === null" ref="container" class="vs-container">
<div ref="rowsContainer"
diff --git a/apps/files_versions/src/css/versions.css b/apps/files_versions/src/css/versions.css
index 80aeaf4b88a..1637394ef48 100644
--- a/apps/files_versions/src/css/versions.css
+++ b/apps/files_versions/src/css/versions.css
@@ -1,3 +1,8 @@
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
.versionsTabView .clear-float {
clear: both;
}
@@ -36,16 +41,16 @@
.versionsTabView img {
cursor: pointer;
- padding-right: 4px;
+ padding-inline-end: 4px;
}
.versionsTabView img.preview {
position: relative;
top: 6px;
- left: 10px;
+ inset-inline-start: 10px;
border: 1px solid var(--color-border-dark);
cursor: default;
- padding-right: 0;
+ padding-inline-end: 0;
}
.versionsTabView .version-container {
@@ -58,7 +63,7 @@
}
.versionsTabView .version-details {
- text-align: left;
+ text-align: start;
}
.versionsTabView .version-details > span {
@@ -68,7 +73,7 @@
.versionsTabView .revertVersion {
cursor: pointer;
float: right;
- margin-right: 0;
+ margin-inline-end: 0;
}
.versionsTabView li.active .downloadVersion {
@@ -90,7 +95,7 @@
}
.version-container {
- padding-left: 5px;
+ padding-inline-start: 5px;
}
.version-details {
diff --git a/apps/files_versions/src/files_versions_tab.js b/apps/files_versions/src/files_versions_tab.js
index a341b9009c3..12f36bad24a 100644
--- a/apps/files_versions/src/files_versions_tab.js
+++ b/apps/files_versions/src/files_versions_tab.js
@@ -1,34 +1,20 @@
/**
- * @copyright 2022 Carl Schwan <carl@carlschwan.eu>
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import Vue from 'vue'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import VersionTab from './views/VersionTab.vue'
-import VTooltip from 'v-tooltip'
+import VTooltipPlugin from 'v-tooltip'
// eslint-disable-next-line n/no-missing-import, import/no-unresolved
import BackupRestore from '@mdi/svg/svg/backup-restore.svg?raw'
Vue.prototype.t = t
Vue.prototype.n = n
-Vue.use(VTooltip)
+Vue.use(VTooltipPlugin)
// Init Sharing tab component
const View = Vue.extend(VersionTab)
diff --git a/apps/files_versions/src/utils/davClient.js b/apps/files_versions/src/utils/davClient.js
index b091935fc31..029373e9193 100644
--- a/apps/files_versions/src/utils/davClient.js
+++ b/apps/files_versions/src/utils/davClient.js
@@ -1,22 +1,6 @@
/**
- * @copyright 2022 Louis Chemineau <mlouis@chmn.me>
- *
- * @author Louis Chemineau <mlouis@chmn.me>
- *
- * @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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { createClient } from 'webdav'
@@ -30,16 +14,16 @@ const client = createClient(remote)
// set CSRF token header
const setHeaders = (token) => {
- client.setHeaders({
- // Add this so the server knows it is an request from the browser
- 'X-Requested-With': 'XMLHttpRequest',
- // Inject user auth
- requesttoken: token ?? '',
- })
+ client.setHeaders({
+ // Add this so the server knows it is an request from the browser
+ 'X-Requested-With': 'XMLHttpRequest',
+ // Inject user auth
+ requesttoken: token ?? '',
+ })
}
// refresh headers when request token changes
onRequestTokenUpdate(setHeaders)
setHeaders(getRequestToken())
-export default client \ No newline at end of file
+export default client
diff --git a/apps/files_versions/src/utils/davRequest.js b/apps/files_versions/src/utils/davRequest.js
index 93f1861006b..1dcf620564a 100644
--- a/apps/files_versions/src/utils/davRequest.js
+++ b/apps/files_versions/src/utils/davRequest.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2019 Louis Chmn <louis@chmn.me>
- *
- * @author Louis Chmn <louis@chmn.me>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
export default `<?xml version="1.0"?>
diff --git a/apps/files_versions/src/utils/logger.js b/apps/files_versions/src/utils/logger.js
index 4f0356764d9..f84cb969244 100644
--- a/apps/files_versions/src/utils/logger.js
+++ b/apps/files_versions/src/utils/logger.js
@@ -1,22 +1,6 @@
/**
- * @copyright 2022 Louis Chemineau <mlouis@chmn.me>
- *
- * @author Louis Chemineau <mlouis@chmn.me>
- *
- * @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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { getLoggerBuilder } from '@nextcloud/logger'
diff --git a/apps/files_versions/src/utils/versions.ts b/apps/files_versions/src/utils/versions.ts
index 4048f23c327..6d5933f0bd9 100644
--- a/apps/files_versions/src/utils/versions.ts
+++ b/apps/files_versions/src/utils/versions.ts
@@ -2,24 +2,8 @@
/* eslint-disable jsdoc/require-param */
/* eslint-disable jsdoc/require-jsdoc */
/**
- * @copyright 2022 Louis Chemineau <mlouis@chmn.me>
- *
- * @author Louis Chemineau <mlouis@chmn.me>
- *
- * @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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { FileStat, ResponseDataDetailed } from 'webdav'
@@ -44,7 +28,6 @@ export interface Version {
type: string, // 'file'
mtime: number, // Version creation date as a timestamp
permissions: string, // Only readable: 'R'
- hasPreview: boolean, // Whether the version has a preview
previewUrl: string, // Preview URL of the version
url: string, // Download URL of the version
source: string, // The WebDAV endpoint of the ressource
@@ -94,12 +77,12 @@ function formatVersion(version: any, fileInfo: any): Version {
let previewUrl = ''
if (mtime === fileInfo.mtime) { // Version is the current one
- previewUrl = generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0', {
+ previewUrl = generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0&forceIcon=1&mimeFallback=1', {
fileId: fileInfo.id,
fileEtag: fileInfo.etag,
})
} else {
- previewUrl = generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', {
+ previewUrl = generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}&mimeFallback=1', {
file: joinPaths(fileInfo.path, fileInfo.name),
fileVersion: version.basename,
})
@@ -107,7 +90,8 @@ function formatVersion(version: any, fileInfo: any): Version {
return {
fileId: fileInfo.id,
- label: version.props['version-label'],
+ // If version-label is defined make sure it is a string (prevent issue if the label is a number an PHP returns a number then)
+ label: version.props['version-label'] && String(version.props['version-label']),
author: version.props['version-author'] ?? null,
filename: version.filename,
basename: moment(mtime).format('LLL'),
@@ -117,7 +101,6 @@ function formatVersion(version: any, fileInfo: any): Version {
type: version.type,
mtime,
permissions: 'R',
- hasPreview: version.props['has-preview'] === 1,
previewUrl,
url: joinPaths('/remote.php/dav', version.filename),
source: generateRemoteUrl('dav') + encodePath(version.filename),
diff --git a/apps/files_versions/src/views/VersionTab.vue b/apps/files_versions/src/views/VersionTab.vue
index 745b9d0f58e..a643aef439d 100644
--- a/apps/files_versions/src/views/VersionTab.vue
+++ b/apps/files_versions/src/views/VersionTab.vue
@@ -1,81 +1,68 @@
<!--
- - @copyright 2022 Carl Schwan <carl@carlschwan.eu>
- - @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/>.
- -->
+ - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<div class="versions-tab__container">
- <VirtualScrolling :sections="sections"
+ <VirtualScrolling v-slot="{ visibleSections }"
+ :sections="sections"
:header-height="0">
- <template slot-scope="{visibleSections}">
- <ul data-files-versions-versions-list>
- <template v-if="visibleSections.length === 1">
- <Version v-for="(row) of visibleSections[0].rows"
- :key="row.items[0].mtime"
- :can-view="canView"
- :can-compare="canCompare"
- :load-preview="isActive"
- :version="row.items[0]"
- :file-info="fileInfo"
- :is-current="row.items[0].mtime === fileInfo.mtime"
- :is-first-version="row.items[0].mtime === initialVersionMtime"
- @click="openVersion"
- @compare="compareVersion"
- @restore="handleRestore"
- @label-update-request="handleLabelUpdateRequest(row.items[0])"
- @delete="handleDelete" />
- </template>
- </ul>
- </template>
+ <ul :aria-label="t('files_versions', 'File versions')" data-files-versions-versions-list>
+ <template v-if="visibleSections.length === 1">
+ <Version v-for="(row) of visibleSections[0].rows"
+ :key="row.items[0].mtime"
+ :can-view="canView"
+ :can-compare="canCompare"
+ :load-preview="isActive"
+ :version="row.items[0]"
+ :file-info="fileInfo"
+ :is-current="row.items[0].mtime === fileInfo.mtime"
+ :is-first-version="row.items[0].mtime === initialVersionMtime"
+ @click="openVersion"
+ @compare="compareVersion"
+ @restore="handleRestore"
+ @label-update-request="handleLabelUpdateRequest(row.items[0])"
+ @delete="handleDelete" />
+ </template>
+ </ul>
<NcLoadingIcon v-if="loading" slot="loader" class="files-list-viewer__loader" />
</VirtualScrolling>
- <NcModal v-if="showVersionLabelForm"
- :title="t('files_versions', 'Name this version')"
- @close="showVersionLabelForm = false">
- <VersionLabelForm :version-label="editedVersion.label" @label-update="handleLabelUpdate" />
- </NcModal>
+ <VersionLabelDialog v-if="editedVersion"
+ :open.sync="showVersionLabelForm"
+ :version-label="editedVersion.label"
+ @label-update="handleLabelUpdate" />
</div>
</template>
<script>
import path from 'path'
+import { getCurrentUser } from '@nextcloud/auth'
import { showError, showSuccess } from '@nextcloud/dialogs'
-import isMobile from '@nextcloud/vue/dist/Mixins/isMobile.js'
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
-import { getCurrentUser } from '@nextcloud/auth'
-import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
-import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
+import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile'
+import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
import { fetchVersions, deleteVersion, restoreVersion, setVersionLabel } from '../utils/versions.ts'
import Version from '../components/Version.vue'
import VirtualScrolling from '../components/VirtualScrolling.vue'
-import VersionLabelForm from '../components/VersionLabelForm.vue'
+import VersionLabelDialog from '../components/VersionLabelDialog.vue'
export default {
name: 'VersionTab',
components: {
Version,
VirtualScrolling,
- VersionLabelForm,
+ VersionLabelDialog,
NcLoadingIcon,
- NcModal,
},
- mixins: [
- isMobile,
- ],
+
+ setup() {
+ return {
+ isMobile: useIsMobile(),
+ }
+ },
+
data() {
return {
fileInfo: null,
@@ -84,6 +71,7 @@ export default {
versions: [],
loading: false,
showVersionLabelForm: false,
+ editedVersion: null,
}
},
computed: {
@@ -192,7 +180,7 @@ export default {
/**
* Handle restored event from Version.vue
*
- * @param {import('../utils/versions.ts').Version} version
+ * @param {import('../utils/versions.ts').Version} version The version to restore
*/
async handleRestore(version) {
// Update local copy of fileInfo as rendering depends on it.
@@ -215,7 +203,7 @@ export default {
try {
await restoreVersion(version)
- if (version.label !== '') {
+ if (version.label) {
showSuccess(t('files_versions', `${version.label} restored`))
} else if (version.mtime === this.initialVersionMtime) {
showSuccess(t('files_versions', 'Initial version restored'))
@@ -232,7 +220,7 @@ export default {
/**
* Handle label-updated event from Version.vue
- * @param {import('../utils/versions.ts').Version} version
+ * @param {import('../utils/versions.ts').Version} version The version to update
*/
handleLabelUpdateRequest(version) {
this.showVersionLabelForm = true
@@ -241,7 +229,7 @@ export default {
/**
* Handle label-updated event from Version.vue
- * @param {string} newLabel
+ * @param {string} newLabel The new label
*/
async handleLabelUpdate(newLabel) {
const oldLabel = this.editedVersion.label
@@ -261,8 +249,7 @@ export default {
/**
* Handle deleted event from Version.vue
*
- * @param {import('../utils/versions.ts').Version} version
- * @param {string} newName
+ * @param {import('../utils/versions.ts').Version} version The version to delete
*/
async handleDelete(version) {
const index = this.versions.indexOf(version)
@@ -290,13 +277,12 @@ export default {
return
}
- // Versions previews are too small for our use case, so we override hasPreview and previewUrl
+ // Versions previews are too small for our use case, so we override previewUrl
// which makes the viewer render the original file.
// We also point to the original filename if the version is the current one.
const versions = this.versions.map(version => ({
...version,
filename: version.mtime === this.fileInfo.mtime ? path.join('files', getCurrentUser()?.uid ?? '', this.fileInfo.path, this.fileInfo.name) : version.filename,
- hasPreview: false,
previewUrl: undefined,
}))
@@ -307,7 +293,7 @@ export default {
},
compareVersion({ version }) {
- const versions = this.versions.map(version => ({ ...version, hasPreview: false, previewUrl: undefined }))
+ const versions = this.versions.map(version => ({ ...version, previewUrl: undefined }))
OCA.Viewer.compare(this.viewerFileInfo, versions.find(v => v.source === version.source))
},