Skip to content

Commit 46d561d

Browse files
committed
fix(files_versions): correctly show version author also for shared files
The users endpoint is not available for other users if the current user has no admin privileges, so instead use the displaynames endpoint. Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 927739c commit 46d561d

File tree

4 files changed

+112
-43
lines changed

4 files changed

+112
-43
lines changed

apps/files_versions/src/components/Version.vue

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,19 @@
3131
<div class="version__info">
3232
<div v-if="versionLabel"
3333
class="version__info__label"
34+
data-cy-files-version-label
3435
:title="versionLabel">
3536
{{ versionLabel }}
3637
</div>
37-
<div v-if="versionAuthor" class="version__info">
38+
<div v-if="versionAuthor"
39+
class="version__info"
40+
data-cy-files-version-author-name>
3841
<span v-if="versionLabel">•</span>
3942
<NcAvatar class="avatar"
4043
:user="version.author"
41-
:size="16"
42-
:disable-menu="true"
43-
:disable-tooltip="true"
44+
:size="20"
45+
disable-menu
46+
disable-tooltip
4447
:show-user-status="false" />
4548
<div>{{ versionAuthor }}</div>
4649
</div>
@@ -53,7 +56,7 @@
5356
<NcDateTime class="version__info__date"
5457
relative-time="short"
5558
:timestamp="version.mtime" />
56-
<!-- Separate dot to improve alignement -->
59+
<!-- Separate dot to improve alignment -->
5760
<span>•</span>
5861
<span>{{ humanReadableSize }}</span>
5962
</div>
@@ -114,8 +117,18 @@
114117
import type { PropType } from 'vue'
115118
import type { Version } from '../utils/versions'
116119
120+
import { getCurrentUser } from '@nextcloud/auth'
121+
import { Permission, formatFileSize } from '@nextcloud/files'
122+
import { loadState } from '@nextcloud/initial-state'
123+
import { t } from '@nextcloud/l10n'
124+
import { joinPaths } from '@nextcloud/paths'
125+
import { getRootUrl, generateUrl } from '@nextcloud/router'
117126
import { defineComponent } from 'vue'
118127
128+
import axios from '@nextcloud/axios'
129+
import moment from '@nextcloud/moment'
130+
import logger from '../utils/logger'
131+
119132
import BackupRestore from 'vue-material-design-icons/BackupRestore.vue'
120133
import Delete from 'vue-material-design-icons/Delete.vue'
121134
import Download from 'vue-material-design-icons/Download.vue'
@@ -130,14 +143,6 @@ import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
130143
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
131144
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'
132145
133-
import moment from '@nextcloud/moment'
134-
import { getRootUrl, generateOcsUrl } from '@nextcloud/router'
135-
import { joinPaths } from '@nextcloud/paths'
136-
import { loadState } from '@nextcloud/initial-state'
137-
import { Permission, formatFileSize } from '@nextcloud/files'
138-
import { translate as t } from '@nextcloud/l10n'
139-
import axios from '@nextcloud/axios'
140-
141146
const hasPermission = (permissions: number, permission: number): boolean => (permissions & permission) !== 0
142147
143148
export default defineComponent({
@@ -199,7 +204,7 @@ export default defineComponent({
199204
previewLoaded: false,
200205
previewErrored: false,
201206
capabilities: loadState('core', 'capabilities', { files: { version_labeling: false, version_deletion: false } }),
202-
versionAuthor: '',
207+
versionAuthor: '' as string | null,
203208
}
204209
},
205210
@@ -295,21 +300,26 @@ export default defineComponent({
295300
},
296301
297302
async fetchDisplayName() {
298-
// check to make sure that we have a valid author - in case database did not migrate, null author, etc.
299-
if (this.version.author) {
303+
this.versionAuthor = null
304+
if (!this.version.author) {
305+
return
306+
}
307+
308+
if (this.version.author === getCurrentUser()?.uid) {
309+
this.versionAuthor = t('files_versions', 'You')
310+
} else {
300311
try {
301-
const { data } = await axios.get(generateOcsUrl(`/cloud/users/${this.version.author}`))
302-
this.versionAuthor = data.ocs.data.displayname
303-
} catch (e) {
304-
// Promise got rejected - default to null author to not try to load author profile
305-
this.versionAuthor = null
312+
const { data } = await axios.post(generateUrl('/displaynames'), { users: [this.version.author] })
313+
this.versionAuthor = data.users[this.version.author]
314+
} catch (error) {
315+
logger.warn('Could not load user display name', { error })
306316
}
307317
}
308318
},
309319
310320
click() {
311321
if (!this.canView) {
312-
window.location = this.downloadURL
322+
window.location.href = this.downloadURL
313323
return
314324
}
315325
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
@@ -4,28 +4,27 @@
44
-->
55
<template>
66
<div class="versions-tab__container">
7-
<VirtualScrolling :sections="sections"
7+
<VirtualScrolling v-slot="{ visibleSections }"
8+
:sections="sections"
89
:header-height="0">
9-
<template slot-scope="{visibleSections}">
10-
<ul data-files-versions-versions-list>
11-
<template v-if="visibleSections.length === 1">
12-
<Version v-for="(row) of visibleSections[0].rows"
13-
:key="row.items[0].mtime"
14-
:can-view="canView"
15-
:can-compare="canCompare"
16-
:load-preview="isActive"
17-
:version="row.items[0]"
18-
:file-info="fileInfo"
19-
:is-current="row.items[0].mtime === fileInfo.mtime"
20-
:is-first-version="row.items[0].mtime === initialVersionMtime"
21-
@click="openVersion"
22-
@compare="compareVersion"
23-
@restore="handleRestore"
24-
@label-update-request="handleLabelUpdateRequest(row.items[0])"
25-
@delete="handleDelete" />
26-
</template>
27-
</ul>
28-
</template>
10+
<ul :aria-label="t('files_versions', 'File versions')" data-files-versions-versions-list>
11+
<template v-if="visibleSections.length === 1">
12+
<Version v-for="(row) of visibleSections[0].rows"
13+
:key="row.items[0].mtime"
14+
:can-view="canView"
15+
:can-compare="canCompare"
16+
:load-preview="isActive"
17+
:version="row.items[0]"
18+
:file-info="fileInfo"
19+
:is-current="row.items[0].mtime === fileInfo.mtime"
20+
:is-first-version="row.items[0].mtime === initialVersionMtime"
21+
@click="openVersion"
22+
@compare="compareVersion"
23+
@restore="handleRestore"
24+
@label-update-request="handleLabelUpdateRequest(row.items[0])"
25+
@delete="handleDelete" />
26+
</template>
27+
</ul>
2928
<NcLoadingIcon v-if="loading" slot="loader" class="files-list-viewer__loader" />
3029
</VirtualScrolling>
3130
<VersionLabelDialog v-if="editedVersion"

cypress/e2e/files_versions/version_creation.cy.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,18 @@ describe('Versions creation', () => {
3030
cy.get('[data-files-versions-version]').eq(2).contains('Initial version')
3131
})
3232
})
33+
34+
it('See yourself as version author', () => {
35+
cy.visit('/apps/files')
36+
openVersionsPanel(randomFileName)
37+
38+
cy.findByRole('tabpanel', { name: 'Versions' })
39+
.findByRole('list', { name: 'File versions' })
40+
.findAllByRole('listitem')
41+
.should('have.length', 3)
42+
.first()
43+
.find('[data-cy-files-version-author-name]')
44+
.should('exist')
45+
.and('contain.text', 'You')
46+
})
3347
})
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+
})

0 commit comments

Comments
 (0)