Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
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 <[email protected]>
  • Loading branch information
susnux committed Mar 26, 2025
commit a140d9f2617bb05e0b683cb43b2366e8b3076f63
56 changes: 33 additions & 23 deletions apps/files_versions/src/components/Version.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,19 @@
<div class="version__info">
<div v-if="versionLabel"
class="version__info__label"
data-cy-files-version-label
:title="versionLabel">
{{ versionLabel }}
</div>
<div v-if="versionAuthor" class="version__info">
<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>
Expand All @@ -52,7 +55,7 @@
<NcDateTime class="version__info__date"
relative-time="short"
:timestamp="version.mtime" />
<!-- Separate dot to improve alignement -->
<!-- Separate dot to improve alignment -->
<span>•</span>
<span>{{ humanReadableSize }}</span>
</div>
Expand Down Expand Up @@ -114,6 +117,17 @@
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 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'
Expand All @@ -128,15 +142,6 @@ import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'

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 { defineComponent } from 'vue'

import axios from '@nextcloud/axios'

const hasPermission = (permissions: number, permission: number): boolean => (permissions & permission) !== 0

export default defineComponent({
Expand Down Expand Up @@ -198,7 +203,7 @@ export default defineComponent({
previewLoaded: false,
previewErrored: false,
capabilities: loadState('core', 'capabilities', { files: { version_labeling: false, version_deletion: false } }),
versionAuthor: '',
versionAuthor: '' as string|null,
}
},

Expand Down Expand Up @@ -290,21 +295,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 })
Expand Down
41 changes: 20 additions & 21 deletions apps/files_versions/src/views/VersionTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,27 @@
-->
<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>
<VersionLabelDialog v-if="editedVersion"
Expand Down
14 changes: 14 additions & 0 deletions cypress/e2e/files_versions/version_creation.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,18 @@ describe('Versions creation', () => {
cy.get('[data-files-versions-version]').eq(2).contains('Initial version')
})
})

it('See yourself as version author', () => {
cy.visit('/apps/files')
openVersionsPanel(randomFileName)

cy.findByRole('tabpanel', { name: 'Versions' })
.findByRole('list', { name: 'File versions' })
.findAllByRole('listitem')
.should('have.length', 3)
.first()
.find('[data-cy-files-version-author-name]')
.should('exist')
.and('contain.text', 'You')
})
})
46 changes: 46 additions & 0 deletions cypress/e2e/files_versions/version_sharing.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { User } from '@nextcloud/cypress'
import { openVersionsPanel, setupTestSharedFileFromUser, uploadThreeVersions } from './filesVersionsUtils.ts'
import { navigateToFolder, triggerActionForFile } from '../files/FilesUtils.ts'

describe('Versions on shares', () => {
const randomSharedFolderName = Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, 10)
const randomFileName = Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, 10) + '.txt'
const randomFilePath = `${randomSharedFolderName}/${randomFileName}`
let alice: User
let bob: User

before(() => {
cy.createRandomUser()
.then((user) => {
alice = user
})
.then(() => {
cy.mkdir(alice, `/${randomSharedFolderName}`)
return setupTestSharedFileFromUser(alice, randomSharedFolderName, {})
})
.then((user) => { bob = user })
.then(() => uploadThreeVersions(alice, randomFilePath))
})

it('See sharees display name as author', () => {
cy.login(bob)
cy.visit('/apps/files')

navigateToFolder(randomSharedFolderName)

triggerActionForFile(randomFileName, 'details')
cy.findByRole('tab', { name: 'Versions' }).click()

cy.findByRole('tabpanel', { name: 'Versions' })
.findByRole('list', { name: 'File versions' })
.findAllByRole('listitem')
.first()
.find('[data-cy-files-version-author-name]')
.should('be.visible')
.and('contain.text', alice.userId)
})
})
Loading