Skip to content

Commit 43cac4d

Browse files
authored
Merge pull request #51721 from nextcloud/backport/51705/stable29
[stable29] fix(files_versions): correctly show version author also for shared files
2 parents ec4187c + 7c0aed5 commit 43cac4d

File tree

6 files changed

+116
-47
lines changed

6 files changed

+116
-47
lines changed

apps/files_versions/src/components/Version.vue

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,19 @@
4343
<div class="version__info">
4444
<div v-if="versionLabel"
4545
class="version__info__label"
46+
data-cy-files-version-label
4647
:title="versionLabel">
4748
{{ versionLabel }}
4849
</div>
49-
<div v-if="versionAuthor" class="version__info">
50+
<div v-if="versionAuthor"
51+
class="version__info"
52+
data-cy-files-version-author-name>
5053
<span v-if="versionLabel">•</span>
5154
<NcAvatar class="avatar"
5255
:user="version.author"
53-
:size="16"
54-
:disable-menu="true"
55-
:disable-tooltip="true"
56+
:size="20"
57+
disable-menu
58+
disable-tooltip
5659
:show-user-status="false" />
5760
<div>{{ versionAuthor }}</div>
5861
</div>
@@ -65,7 +68,7 @@
6568
<NcDateTime class="version__info__date"
6669
relative-time="short"
6770
:timestamp="version.mtime" />
68-
<!-- Separate dot to improve alignement -->
71+
<!-- Separate dot to improve alignment -->
6972
<span>•</span>
7073
<span>{{ humanReadableSize }}</span>
7174
</div>
@@ -127,6 +130,17 @@
127130
import type { PropType } from 'vue'
128131
import type { Version } from '../utils/versions'
129132
133+
import { getCurrentUser } from '@nextcloud/auth'
134+
import { Permission, formatFileSize } from '@nextcloud/files'
135+
import { loadState } from '@nextcloud/initial-state'
136+
import { t } from '@nextcloud/l10n'
137+
import { joinPaths } from '@nextcloud/paths'
138+
import { getRootUrl, generateUrl } from '@nextcloud/router'
139+
import { defineComponent } from 'vue'
140+
141+
import axios from '@nextcloud/axios'
142+
import logger from '../utils/logger'
143+
130144
import BackupRestore from 'vue-material-design-icons/BackupRestore.vue'
131145
import Delete from 'vue-material-design-icons/Delete.vue'
132146
import Download from 'vue-material-design-icons/Download.vue'
@@ -141,15 +155,6 @@ import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
141155
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
142156
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'
143157
144-
import { getRootUrl, generateOcsUrl } from '@nextcloud/router'
145-
import { joinPaths } from '@nextcloud/paths'
146-
import { loadState } from '@nextcloud/initial-state'
147-
import { Permission, formatFileSize } from '@nextcloud/files'
148-
import { translate as t } from '@nextcloud/l10n'
149-
import { defineComponent } from 'vue'
150-
151-
import axios from '@nextcloud/axios'
152-
153158
const hasPermission = (permissions: number, permission: number): boolean => (permissions & permission) !== 0
154159
155160
export default defineComponent({
@@ -211,7 +216,7 @@ export default defineComponent({
211216
previewLoaded: false,
212217
previewErrored: false,
213218
capabilities: loadState('core', 'capabilities', { files: { version_labeling: false, version_deletion: false } }),
214-
versionAuthor: '',
219+
versionAuthor: '' as string | null,
215220
}
216221
},
217222
@@ -303,21 +308,26 @@ export default defineComponent({
303308
},
304309
305310
async fetchDisplayName() {
306-
// check to make sure that we have a valid author - in case database did not migrate, null author, etc.
307-
if (this.version.author) {
311+
this.versionAuthor = null
312+
if (!this.version.author) {
313+
return
314+
}
315+
316+
if (this.version.author === getCurrentUser()?.uid) {
317+
this.versionAuthor = t('files_versions', 'You')
318+
} else {
308319
try {
309-
const { data } = await axios.get(generateOcsUrl(`/cloud/users/${this.version.author}`))
310-
this.versionAuthor = data.ocs.data.displayname
311-
} catch (e) {
312-
// Promise got rejected - default to null author to not try to load author profile
313-
this.versionAuthor = null
320+
const { data } = await axios.post(generateUrl('/displaynames'), { users: [this.version.author] })
321+
this.versionAuthor = data.users[this.version.author]
322+
} catch (error) {
323+
logger.warn('Could not load user display name', { error })
314324
}
315325
}
316326
},
317327
318328
click() {
319329
if (!this.canView) {
320-
window.location = this.downloadURL
330+
window.location.href = this.downloadURL
321331
return
322332
}
323333
this.$emit('click', { version: this.version })

apps/files_versions/src/views/VersionTab.vue

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,27 @@
1717
-->
1818
<template>
1919
<div class="versions-tab__container">
20-
<VirtualScrolling :sections="sections"
20+
<VirtualScrolling v-slot="{ visibleSections }"
21+
:sections="sections"
2122
:header-height="0">
22-
<template slot-scope="{visibleSections}">
23-
<ul data-files-versions-versions-list>
24-
<template v-if="visibleSections.length === 1">
25-
<Version v-for="(row) of visibleSections[0].rows"
26-
:key="row.items[0].mtime"
27-
:can-view="canView"
28-
:can-compare="canCompare"
29-
:load-preview="isActive"
30-
:version="row.items[0]"
31-
:file-info="fileInfo"
32-
:is-current="row.items[0].mtime === fileInfo.mtime"
33-
:is-first-version="row.items[0].mtime === initialVersionMtime"
34-
@click="openVersion"
35-
@compare="compareVersion"
36-
@restore="handleRestore"
37-
@label-update-request="handleLabelUpdateRequest(row.items[0])"
38-
@delete="handleDelete" />
39-
</template>
40-
</ul>
41-
</template>
23+
<ul :aria-label="t('files_versions', 'File versions')" data-files-versions-versions-list>
24+
<template v-if="visibleSections.length === 1">
25+
<Version v-for="(row) of visibleSections[0].rows"
26+
:key="row.items[0].mtime"
27+
:can-view="canView"
28+
:can-compare="canCompare"
29+
:load-preview="isActive"
30+
:version="row.items[0]"
31+
:file-info="fileInfo"
32+
:is-current="row.items[0].mtime === fileInfo.mtime"
33+
:is-first-version="row.items[0].mtime === initialVersionMtime"
34+
@click="openVersion"
35+
@compare="compareVersion"
36+
@restore="handleRestore"
37+
@label-update-request="handleLabelUpdateRequest(row.items[0])"
38+
@delete="handleDelete" />
39+
</template>
40+
</ul>
4241
<NcLoadingIcon v-if="loading" slot="loader" class="files-list-viewer__loader" />
4342
</VirtualScrolling>
4443
<NcModal v-if="showVersionLabelForm"

cypress/e2e/files_versions/version_creation.cy.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,18 @@ describe('Versions creation', () => {
4747
cy.get('[data-files-versions-version]').eq(2).contains('Initial version')
4848
})
4949
})
50+
51+
it('See yourself as version author', () => {
52+
cy.visit('/apps/files')
53+
openVersionsPanel(randomFileName)
54+
55+
cy.findByRole('tabpanel', { name: 'Versions' })
56+
.findByRole('list', { name: 'File versions' })
57+
.findAllByRole('listitem')
58+
.should('have.length', 3)
59+
.first()
60+
.find('[data-cy-files-version-author-name]')
61+
.should('exist')
62+
.and('contain.text', 'You')
63+
})
5064
})
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
import type { User } from '@nextcloud/cypress'
6+
import { openVersionsPanel, setupTestSharedFileFromUser, uploadThreeVersions } from './filesVersionsUtils.ts'
7+
import { navigateToFolder, triggerActionForFile } from '../files/FilesUtils.ts'
8+
9+
describe('Versions on shares', () => {
10+
const randomSharedFolderName = Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, 10)
11+
const randomFileName = Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, 10) + '.txt'
12+
const randomFilePath = `${randomSharedFolderName}/${randomFileName}`
13+
let alice: User
14+
let bob: User
15+
16+
before(() => {
17+
cy.createRandomUser()
18+
.then((user) => {
19+
alice = user
20+
})
21+
.then(() => {
22+
cy.mkdir(alice, `/${randomSharedFolderName}`)
23+
return setupTestSharedFileFromUser(alice, randomSharedFolderName, {})
24+
})
25+
.then((user) => { bob = user })
26+
.then(() => uploadThreeVersions(alice, randomFilePath))
27+
})
28+
29+
it('See sharees display name as author', () => {
30+
cy.login(bob)
31+
cy.visit('/apps/files')
32+
33+
navigateToFolder(randomSharedFolderName)
34+
35+
triggerActionForFile(randomFileName, 'details')
36+
cy.findByRole('tab', { name: 'Versions' }).click()
37+
38+
cy.findByRole('tabpanel', { name: 'Versions' })
39+
.findByRole('list', { name: 'File versions' })
40+
.findAllByRole('listitem')
41+
.first()
42+
.find('[data-cy-files-version-author-name]')
43+
.should('be.visible')
44+
.and('contain.text', alice.userId)
45+
})
46+
})

dist/files_versions-files_versions.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files_versions-files_versions.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)