Merge pull request #51705 from nextcloud/fix/files-versions

fix(files_versions): correctly show version author also for shared files
pull/51724/head
Ferdinand Thiessen 2025-03-26 10:16:15 +07:00 committed by GitHub
commit c85b8aa36c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 114 additions and 45 deletions

@ -31,16 +31,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>
@ -53,7 +56,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>
@ -114,8 +117,18 @@
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'
@ -130,14 +143,6 @@ import NcDateTime from '@nextcloud/vue/components/NcDateTime'
import NcListItem from '@nextcloud/vue/components/NcListItem'
import Tooltip from '@nextcloud/vue/directives/Tooltip'
import { Permission, formatFileSize } from '@nextcloud/files'
import { loadState } from '@nextcloud/initial-state'
import { t } from '@nextcloud/l10n'
import { joinPaths } from '@nextcloud/paths'
import { getRootUrl, generateOcsUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import moment from '@nextcloud/moment'
const hasPermission = (permissions: number, permission: number): boolean => (permissions & permission) !== 0
export default defineComponent({
@ -295,21 +300,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 })

@ -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"

@ -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')
})
})

@ -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)
})
})

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long