Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 4 additions & 4 deletions apps/files_sharing/src/components/SharingEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default {
if (!this.isShareOwner && this.share.ownerDisplayName) {
title += ' ' + t('files_sharing', 'by {initiator}', {
initiator: this.share.ownerDisplayName,
})
}, undefined, { escape: false })
}
return title
},
Expand All @@ -107,12 +107,12 @@ export default {
owner: this.share.ownerDisplayName,
}
if (this.share.type === ShareType.Group) {
return t('files_sharing', 'Shared with the group {user} by {owner}', data)
return t('files_sharing', 'Shared with the group {user} by {owner}', data, undefined, { escape: false })
} else if (this.share.type === ShareType.Room) {
return t('files_sharing', 'Shared with the conversation {user} by {owner}', data)
return t('files_sharing', 'Shared with the conversation {user} by {owner}', data, undefined, { escape: false })
}

return t('files_sharing', 'Shared with {user} by {owner}', data)
return t('files_sharing', 'Shared with {user} by {owner}', data, undefined, { escape: false })
}
return null
},
Expand Down
4 changes: 2 additions & 2 deletions apps/files_sharing/src/components/SharingEntryInherited.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
class="sharing-entry__avatar" />
</template>
<NcActionText icon="icon-user">
{{ t('files_sharing', 'Added by {initiator}', { initiator: share.ownerDisplayName }) }}
{{ t('files_sharing', 'Added by {initiator}', { initiator: share.ownerDisplayName }, undefined, { escape: false }) }}
</NcActionText>
<NcActionLink
v-if="share.viaPath && share.viaFileid"
icon="icon-folder"
:href="viaFileTargetUrl">
{{ t('files_sharing', 'Via “{folder}”', { folder: viaFolderName }) }}
{{ t('files_sharing', 'Via “{folder}”', { folder: viaFolderName }, undefined, { escape: false }) }}
</NcActionLink>
<NcActionButton
v-if="share.canDelete"
Expand Down
109 changes: 109 additions & 0 deletions apps/files_sharing/src/files_actions/sharingStatusAction.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*!
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { IFolder, IView } from '@nextcloud/files'

import { File, Permission } from '@nextcloud/files'
import { ShareType } from '@nextcloud/sharing'
import { beforeAll, describe, expect, test, vi } from 'vitest'
import { action } from './sharingStatusAction.ts'

vi.mock('@nextcloud/auth', () => ({
getCurrentUser: vi.fn(() => ({ uid: 'admin' })),
}))

vi.mock('@nextcloud/sharing/public', () => ({
isPublicShare: vi.fn(() => false),
}))

const view = {
id: 'files',
name: 'Files',
} as IView

beforeAll(() => {
(window as any)._oc_webroot = ''
})

describe('Sharing status action title tests', () => {
test('Title does not double-escape special characters in owner display name', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/SharedFolder',
owner: 'testuser',
mime: 'httpd/unix-directory',
permissions: Permission.ALL,
root: '/files/admin',
attributes: {
'owner-display-name': 'bits & trees',
'share-types': [ShareType.User],
},
})

const title = action.title!({
nodes: [file],
view,
folder: {} as IFolder,
contents: [],
})

expect(title).toBe('Shared by bits & trees')
expect(title).not.toContain('&amp;')
})

test('Title does not double-escape special characters in sharee display name', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/SharedFolder',
owner: 'admin',
mime: 'httpd/unix-directory',
permissions: Permission.ALL | Permission.SHARE,
root: '/files/admin',
attributes: {
'share-types': [ShareType.User],
sharees: {
sharee: [{ id: 'bob', 'display-name': 'Bob & Alice', type: ShareType.User }],
},
},
})

const title = action.title!({
nodes: [file],
view,
folder: {} as IFolder,
contents: [],
})

expect(title).toBe('Shared with Bob & Alice')
expect(title).not.toContain('&amp;')
})

test('Title does not double-escape special characters in group display name', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/SharedFolder',
owner: 'admin',
mime: 'httpd/unix-directory',
permissions: Permission.ALL | Permission.SHARE,
root: '/files/admin',
attributes: {
'share-types': [ShareType.Group],
sharees: {
sharee: [{ id: 'dev-group', 'display-name': 'Dev & Ops', type: ShareType.Group }],
},
},
})

const title = action.title!({
nodes: [file],
view,
folder: {} as IFolder,
contents: [],
})

expect(title).toBe('Shared with group Dev & Ops')
expect(title).not.toContain('&amp;')
})
})
6 changes: 3 additions & 3 deletions apps/files_sharing/src/files_actions/sharingStatusAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const action: IFileAction = {
const node = nodes[0]!
if (node.owner && (node.owner !== getCurrentUser()?.uid || isExternal(node))) {
const ownerDisplayName = node?.attributes?.['owner-display-name']
return t('files_sharing', 'Shared by {ownerDisplayName}', { ownerDisplayName })
return t('files_sharing', 'Shared by {ownerDisplayName}', { ownerDisplayName }, undefined, { escape: false })
}

const shareTypes = Object.values(node?.attributes?.['share-types'] || {}).flat() as number[]
Expand All @@ -64,9 +64,9 @@ export const action: IFileAction = {
const sharee = [sharees].flat()[0] // the property is sometimes weirdly normalized, so we need to compensate
switch (sharee?.type) {
case ShareType.User:
return t('files_sharing', 'Shared with {user}', { user: sharee['display-name'] })
return t('files_sharing', 'Shared with {user}', { user: sharee['display-name'] }, undefined, { escape: false })
case ShareType.Group:
return t('files_sharing', 'Shared with group {group}', { group: sharee['display-name'] ?? sharee.id })
return t('files_sharing', 'Shared with group {group}', { group: sharee['display-name'] ?? sharee.id }, undefined, { escape: false })
default:
return t('files_sharing', 'Shared with others')
}
Expand Down