aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2024-08-16 18:17:24 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2024-08-17 17:59:10 +0200
commit132220a89f302cbe2b9fb8cfa16d384866121fde (patch)
treea8e701c7ed0462f7529c3c80cbffa9e2750cce35
parentad8200d9633f33fcbc9c93d94349a14db82c021f (diff)
downloadnextcloud-server-132220a89f302cbe2b9fb8cfa16d384866121fde.tar.gz
nextcloud-server-132220a89f302cbe2b9fb8cfa16d384866121fde.zip
fix: Access node owner by top level `owner` property
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
-rw-r--r--apps/files/src/components/FileEntry/FileEntryActions.vue1
-rw-r--r--apps/files/src/newMenu/newFolder.ts2
-rw-r--r--apps/files_sharing/src/actions/sharingStatusAction.ts40
-rw-r--r--apps/files_sharing/src/components/FileListFilterAccount.vue2
-rw-r--r--apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue2
-rw-r--r--cypress/e2e/files_sharing/files-inline-action.cy.ts105
6 files changed, 134 insertions, 18 deletions
diff --git a/apps/files/src/components/FileEntry/FileEntryActions.vue b/apps/files/src/components/FileEntry/FileEntryActions.vue
index 2c3444a19fd..06b447295eb 100644
--- a/apps/files/src/components/FileEntry/FileEntryActions.vue
+++ b/apps/files/src/components/FileEntry/FileEntryActions.vue
@@ -35,6 +35,7 @@
:close-after-click="!isMenu(action.id)"
:data-cy-files-list-row-action="action.id"
:is-menu="isMenu(action.id)"
+ :aria-label="action.title?.([source], currentView)"
:title="action.title?.([source], currentView)"
@click="onActionClick(action)">
<template #icon>
diff --git a/apps/files/src/newMenu/newFolder.ts b/apps/files/src/newMenu/newFolder.ts
index 8240f1ffbe5..1e8b2b1cadc 100644
--- a/apps/files/src/newMenu/newFolder.ts
+++ b/apps/files/src/newMenu/newFolder.ts
@@ -59,7 +59,7 @@ export const entry = {
source,
id: fileid,
mtime: new Date(),
- owner: getCurrentUser()?.uid || null,
+ owner: context.owner,
permissions: Permission.ALL,
root: context?.root || '/files/' + getCurrentUser()?.uid,
// Include mount-type from parent folder as this is inherited
diff --git a/apps/files_sharing/src/actions/sharingStatusAction.ts b/apps/files_sharing/src/actions/sharingStatusAction.ts
index 1e25851fa0f..635e9e0d572 100644
--- a/apps/files_sharing/src/actions/sharingStatusAction.ts
+++ b/apps/files_sharing/src/actions/sharingStatusAction.ts
@@ -26,10 +26,9 @@ export const action = new FileAction({
displayName(nodes: Node[]) {
const node = nodes[0]
const shareTypes = Object.values(node?.attributes?.['share-types'] || {}).flat() as number[]
- const ownerId = node?.attributes?.['owner-id']
if (shareTypes.length > 0
- || (ownerId !== getCurrentUser()?.uid || isExternal(node))) {
+ || (node.owner !== getCurrentUser()?.uid || isExternal(node))) {
return t('files_sharing', 'Shared')
}
@@ -38,19 +37,32 @@ export const action = new FileAction({
title(nodes: Node[]) {
const node = nodes[0]
- const ownerId = node?.attributes?.['owner-id']
- const ownerDisplayName = node?.attributes?.['owner-display-name']
- // Mixed share types
- if (Array.isArray(node.attributes?.['share-types']) && node.attributes?.['share-types'].length > 1) {
+ if (node.owner && (node.owner !== getCurrentUser()?.uid || isExternal(node))) {
+ const ownerDisplayName = node?.attributes?.['owner-display-name']
+ return t('files_sharing', 'Shared by {ownerDisplayName}', { ownerDisplayName })
+ }
+
+ const shareTypes = Object.values(node?.attributes?.['share-types'] || {}).flat() as number[]
+ if (shareTypes.length > 1) {
return t('files_sharing', 'Shared multiple times with different people')
}
- if (ownerId && (ownerId !== getCurrentUser()?.uid || isExternal(node))) {
- return t('files_sharing', 'Shared by {ownerDisplayName}', { ownerDisplayName })
+ const sharees = node.attributes.sharees?.sharee as { id: string, 'display-name': string, type: ShareType }[] | undefined
+ if (!sharees) {
+ // No sharees so just show the default message to create a new share
+ return t('files_sharing', 'Show sharing options')
}
- return t('files_sharing', 'Show sharing options')
+ 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'] })
+ case ShareType.Group:
+ return t('files_sharing', 'Shared with group {group}', { group: sharee['display-name'] ?? sharee.id })
+ default:
+ return t('files_sharing', 'Shared with others')
+ }
},
iconSvgInline(nodes: Node[]) {
@@ -69,7 +81,7 @@ export const action = new FileAction({
}
// Group shares
- if (shareTypes.includes(ShareType.Grup)
+ if (shareTypes.includes(ShareType.Group)
|| shareTypes.includes(ShareType.RemoteGroup)) {
return AccountGroupSvg
}
@@ -79,9 +91,8 @@ export const action = new FileAction({
return CircleSvg
}
- const ownerId = node?.attributes?.['owner-id']
- if (ownerId && (ownerId !== getCurrentUser()?.uid || isExternal(node))) {
- return generateAvatarSvg(ownerId, isExternal(node))
+ if (node.owner && (node.owner !== getCurrentUser()?.uid || isExternal(node))) {
+ return generateAvatarSvg(node.owner, isExternal(node))
}
return AccountPlusSvg
@@ -93,7 +104,6 @@ export const action = new FileAction({
}
const node = nodes[0]
- const ownerId = node?.attributes?.['owner-id']
const shareTypes = node.attributes?.['share-types']
const isMixed = Array.isArray(shareTypes) && shareTypes.length > 0
@@ -104,7 +114,7 @@ export const action = new FileAction({
}
// If the node is shared by someone else
- if (ownerId && (ownerId !== getCurrentUser()?.uid || isExternal(node))) {
+ if (node.owner !== getCurrentUser()?.uid || isExternal(node)) {
return true
}
diff --git a/apps/files_sharing/src/components/FileListFilterAccount.vue b/apps/files_sharing/src/components/FileListFilterAccount.vue
index 68383735532..11330c500ab 100644
--- a/apps/files_sharing/src/components/FileListFilterAccount.vue
+++ b/apps/files_sharing/src/components/FileListFilterAccount.vue
@@ -118,7 +118,7 @@ async function updateAvailableAccounts(path: string = '/') {
const { contents } = await currentView.value.getContents(path)
const available = new Map<string, IUserSelectData>()
for (const node of contents) {
- const owner = node.owner ?? node.attributes['owner-id']
+ const owner = node.owner
if (owner && !available.has(owner)) {
available.set(owner, {
id: owner,
diff --git a/apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue b/apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue
index bb71f0455a2..a8a39c41e5d 100644
--- a/apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue
+++ b/apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue
@@ -29,7 +29,7 @@ import NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'
const folder = ref<Folder>()
const note = computed<string>(() => folder.value?.attributes.note ?? '')
const user = computed(() => {
- const id = folder.value?.attributes?.['owner-id']
+ const id = folder.value?.owner
const displayName = folder.value?.attributes?.['owner-display-name']
if (id !== getCurrentUser()?.uid) {
return {
diff --git a/cypress/e2e/files_sharing/files-inline-action.cy.ts b/cypress/e2e/files_sharing/files-inline-action.cy.ts
new file mode 100644
index 00000000000..0fd476ca6ed
--- /dev/null
+++ b/cypress/e2e/files_sharing/files-inline-action.cy.ts
@@ -0,0 +1,105 @@
+/*!
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import type { User } from '@nextcloud/cypress'
+import { createShare } from './FilesSharingUtils.ts'
+import { closeSidebar, getRowForFile } from '../files/FilesUtils.ts'
+
+describe('files_sharing: Files inline status action', { testIsolation: true }, () => {
+ /**
+ * Regression test of https://github.com/nextcloud/server/issues/45723
+ */
+ it('No "shared" tag when user ID is purely numerical', () => {
+ const user = {
+ language: 'en',
+ password: 'test1234',
+ userId: String(Math.floor(Math.random() * 1000)),
+ } as User
+ cy.createUser(user)
+ cy.mkdir(user, '/folder')
+ cy.login(user)
+
+ cy.visit('/apps/files')
+
+ getRowForFile('folder')
+ .should('be.visible')
+ .find('[data-cy-files-list-row-actions]')
+ .findByRole('button', { name: 'Shared' })
+ .should('not.exist')
+ })
+
+ describe('', () => {
+ let user: User
+ let sharee: User
+
+ beforeEach(() => {
+ cy.createRandomUser().then(($user) => {
+ user = $user
+ })
+ cy.createRandomUser().then(($user) => {
+ sharee = $user
+ })
+ })
+
+ it('Render quick option for sharing', () => {
+ cy.mkdir(user, '/folder')
+ cy.login(user)
+
+ cy.visit('/apps/files')
+ getRowForFile('folder')
+ .should('be.visible')
+
+ getRowForFile('folder')
+ .should('be.visible')
+ .find('[data-cy-files-list-row-actions]')
+ .findByRole('button', { name: /Show sharing options/ })
+ .should('be.visible')
+ .click()
+
+ // check the click opened the sidebar
+ cy.get('[data-cy-sidebar]')
+ .should('be.visible')
+ // and ensure the sharing tab is selected
+ .findByRole('tab', { name: 'Sharing', selected: true })
+ .should('exist')
+ })
+
+ it('Render inline status action for sharer', () => {
+ cy.mkdir(user, '/folder')
+ cy.login(user)
+
+ cy.visit('/apps/files')
+ getRowForFile('folder')
+ .should('be.visible')
+ createShare('folder', sharee.userId)
+ closeSidebar()
+
+ getRowForFile('folder')
+ .should('be.visible')
+ .find('[data-cy-files-list-row-actions]')
+ .findByRole('button', { name: /^Shared with/i })
+ .should('be.visible')
+ })
+
+ it('Render inline status action for sharee', () => {
+ cy.mkdir(user, '/folder')
+ cy.login(user)
+
+ cy.visit('/apps/files')
+ getRowForFile('folder')
+ .should('be.visible')
+ createShare('folder', sharee.userId)
+ closeSidebar()
+
+ cy.login(sharee)
+ cy.visit('/apps/files')
+
+ getRowForFile('folder')
+ .should('be.visible')
+ .find('[data-cy-files-list-row-actions]')
+ .findByRole('button', { name: `Shared by ${user.userId}` })
+ .should('be.visible')
+ })
+ })
+})