mirror of
https://github.com/nextcloud/server.git
synced 2026-06-09 00:32:29 -04:00
Merge pull request #47305 from nextcloud/backport/47287/stable30
This commit is contained in:
commit
eb1a26e530
19 changed files with 149 additions and 33 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
return t('files_sharing', 'Shared multiple times with different people')
|
||||
}
|
||||
|
||||
if (ownerId && (ownerId !== getCurrentUser()?.uid || isExternal(node))) {
|
||||
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', 'Show sharing options')
|
||||
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')
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
105
cypress/e2e/files_sharing/files-inline-action.cy.ts
Normal file
105
cypress/e2e/files_sharing/files-inline-action.cy.ts
Normal file
|
|
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
2
dist/5804-5804.js
vendored
2
dist/5804-5804.js
vendored
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[5804],{34604:(e,t,n)=>{n.d(t,{A:()=>a});var r=n(71354),o=n.n(r),i=n(76314),s=n.n(i)()(o());s.push([e.id,"\n.note-to-recipient[data-v-77889141] {\n\tmargin-inline: var(--row-height)\n}\n.note-to-recipient__text[data-v-77889141] {\n\t/* respect new lines */\n\twhite-space: pre-line;\n}\n.note-to-recipient__heading[data-v-77889141] {\n\tfont-weight: bold;\n}\n@media screen and (max-width: 512px) {\n.note-to-recipient[data-v-77889141] {\n\t\tmargin-inline: var(--default-grid-baseline);\n}\n}\n","",{version:3,sources:["webpack://./apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue"],names:[],mappings:";AAsDA;CACA;AACA;AAEA;CACA,sBAAA;CACA,qBAAA;AACA;AAEA;CACA,iBAAA;AACA;AAEA;AACA;EACA,2CAAA;AACA;AACA",sourcesContent:["\x3c!--\n - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n--\x3e\n<template>\n\t<NcNoteCard v-if=\"note.length > 0\"\n\t\tclass=\"note-to-recipient\"\n\t\ttype=\"info\">\n\t\t<p v-if=\"user\" class=\"note-to-recipient__heading\">\n\t\t\t{{ t('files_sharing', 'Note from') }}\n\t\t\t<NcUserBubble :user=\"user.id\" :display-name=\"user.displayName\" />\n\t\t</p>\n\t\t<p v-else class=\"note-to-recipient__heading\">\n\t\t\t{{ t('files_sharing', 'Note:') }}\n\t\t</p>\n\t\t<p class=\"note-to-recipient__text\" v-text=\"note\" />\n\t</NcNoteCard>\n</template>\n\n<script setup lang=\"ts\">\nimport type { Folder } from '@nextcloud/files'\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { t } from '@nextcloud/l10n'\nimport { computed, ref } from 'vue'\n\nimport NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'\nimport NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'\n\nconst folder = ref<Folder>()\nconst note = computed<string>(() => folder.value?.attributes.note ?? '')\nconst user = computed(() => {\n\tconst id = folder.value?.attributes?.['owner-id']\n\tconst displayName = folder.value?.attributes?.['owner-display-name']\n\tif (id !== getCurrentUser()?.uid) {\n\t\treturn {\n\t\t\tid,\n\t\t\tdisplayName,\n\t\t}\n\t}\n\treturn null\n})\n\n/**\n * Update the current folder\n * @param newFolder the new folder to show note for\n */\nfunction updateFolder(newFolder: Folder) {\n\tfolder.value = newFolder\n}\n\ndefineExpose({ updateFolder })\n<\/script>\n\n<style scoped>\n.note-to-recipient {\n\tmargin-inline: var(--row-height)\n}\n\n.note-to-recipient__text {\n\t/* respect new lines */\n\twhite-space: pre-line;\n}\n\n.note-to-recipient__heading {\n\tfont-weight: bold;\n}\n\n@media screen and (max-width: 512px) {\n\t.note-to-recipient {\n\t\tmargin-inline: var(--default-grid-baseline);\n\t}\n}\n</style>\n"],sourceRoot:""}]);const a=s},75804:(e,t,n)=>{n.d(t,{default:()=>x});var r=n(85471),o=n(21777),i=n(53334),s=n(80910),a=n(47741);const l=(0,r.pM)({__name:"FilesHeaderNoteToRecipient",setup(e,t){let{expose:n}=t;const l=(0,r.KR)(),d=(0,r.EW)((()=>l.value?.attributes.note??"")),c=(0,r.EW)((()=>{const e=l.value?.attributes?.["owner-id"],t=l.value?.attributes?.["owner-display-name"];return e!==(0,o.HW)()?.uid?{id:e,displayName:t}:null}));function p(e){l.value=e}return n({updateFolder:p}),{__sfc:!0,folder:l,note:d,user:c,updateFolder:p,t:i.t,NcNoteCard:s.A,NcUserBubble:a.N}}});var d=n(85072),c=n.n(d),p=n(97825),u=n.n(p),A=n(77659),m=n.n(A),f=n(55056),_=n.n(f),C=n(10540),h=n.n(C),v=n(41113),N=n.n(v),g=n(34604),b={};b.styleTagTransform=N(),b.setAttributes=_(),b.insert=m().bind(null,"head"),b.domAPI=u(),b.insertStyleElement=h(),c()(g.A,b),g.A&&g.A.locals&&g.A.locals;const x=(0,n(14486).A)(l,(function(){var e=this,t=e._self._c,n=e._self._setupProxy;return n.note.length>0?t(n.NcNoteCard,{staticClass:"note-to-recipient",attrs:{type:"info"}},[n.user?t("p",{staticClass:"note-to-recipient__heading"},[e._v("\n\t\t"+e._s(n.t("files_sharing","Note from"))+"\n\t\t"),t(n.NcUserBubble,{attrs:{user:n.user.id,"display-name":n.user.displayName}})],1):t("p",{staticClass:"note-to-recipient__heading"},[e._v("\n\t\t"+e._s(n.t("files_sharing","Note:"))+"\n\t")]),e._v(" "),t("p",{staticClass:"note-to-recipient__text",domProps:{textContent:e._s(n.note)}})]):e._e()}),[],!1,null,"77889141",null).exports}}]);
|
||||
//# sourceMappingURL=5804-5804.js.map?v=e347ebb5e67ef9049a78
|
||||
1
dist/5804-5804.js.map
vendored
1
dist/5804-5804.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/5804-5804.js.map.license
vendored
1
dist/5804-5804.js.map.license
vendored
|
|
@ -1 +0,0 @@
|
|||
5804-5804.js.license
|
||||
2
dist/9196-9196.js
vendored
Normal file
2
dist/9196-9196.js
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[9196],{4532:(e,t,n)=>{n.d(t,{A:()=>a});var o=n(71354),r=n.n(o),i=n(76314),s=n.n(i)()(r());s.push([e.id,"\n.note-to-recipient[data-v-514f64d7] {\n\tmargin-inline: var(--row-height)\n}\n.note-to-recipient__text[data-v-514f64d7] {\n\t/* respect new lines */\n\twhite-space: pre-line;\n}\n.note-to-recipient__heading[data-v-514f64d7] {\n\tfont-weight: bold;\n}\n@media screen and (max-width: 512px) {\n.note-to-recipient[data-v-514f64d7] {\n\t\tmargin-inline: var(--default-grid-baseline);\n}\n}\n","",{version:3,sources:["webpack://./apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue"],names:[],mappings:";AAsDA;CACA;AACA;AAEA;CACA,sBAAA;CACA,qBAAA;AACA;AAEA;CACA,iBAAA;AACA;AAEA;AACA;EACA,2CAAA;AACA;AACA",sourcesContent:["\x3c!--\n - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n--\x3e\n<template>\n\t<NcNoteCard v-if=\"note.length > 0\"\n\t\tclass=\"note-to-recipient\"\n\t\ttype=\"info\">\n\t\t<p v-if=\"user\" class=\"note-to-recipient__heading\">\n\t\t\t{{ t('files_sharing', 'Note from') }}\n\t\t\t<NcUserBubble :user=\"user.id\" :display-name=\"user.displayName\" />\n\t\t</p>\n\t\t<p v-else class=\"note-to-recipient__heading\">\n\t\t\t{{ t('files_sharing', 'Note:') }}\n\t\t</p>\n\t\t<p class=\"note-to-recipient__text\" v-text=\"note\" />\n\t</NcNoteCard>\n</template>\n\n<script setup lang=\"ts\">\nimport type { Folder } from '@nextcloud/files'\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { t } from '@nextcloud/l10n'\nimport { computed, ref } from 'vue'\n\nimport NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'\nimport NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'\n\nconst folder = ref<Folder>()\nconst note = computed<string>(() => folder.value?.attributes.note ?? '')\nconst user = computed(() => {\n\tconst id = folder.value?.owner\n\tconst displayName = folder.value?.attributes?.['owner-display-name']\n\tif (id !== getCurrentUser()?.uid) {\n\t\treturn {\n\t\t\tid,\n\t\t\tdisplayName,\n\t\t}\n\t}\n\treturn null\n})\n\n/**\n * Update the current folder\n * @param newFolder the new folder to show note for\n */\nfunction updateFolder(newFolder: Folder) {\n\tfolder.value = newFolder\n}\n\ndefineExpose({ updateFolder })\n<\/script>\n\n<style scoped>\n.note-to-recipient {\n\tmargin-inline: var(--row-height)\n}\n\n.note-to-recipient__text {\n\t/* respect new lines */\n\twhite-space: pre-line;\n}\n\n.note-to-recipient__heading {\n\tfont-weight: bold;\n}\n\n@media screen and (max-width: 512px) {\n\t.note-to-recipient {\n\t\tmargin-inline: var(--default-grid-baseline);\n\t}\n}\n</style>\n"],sourceRoot:""}]);const a=s},59196:(e,t,n)=>{n.d(t,{default:()=>b});var o=n(85471),r=n(21777),i=n(53334),s=n(80910),a=n(47741);const l=(0,o.pM)({__name:"FilesHeaderNoteToRecipient",setup(e,t){let{expose:n}=t;const l=(0,o.KR)(),d=(0,o.EW)((()=>l.value?.attributes.note??"")),c=(0,o.EW)((()=>{const e=l.value?.owner,t=l.value?.attributes?.["owner-display-name"];return e!==(0,r.HW)()?.uid?{id:e,displayName:t}:null}));function p(e){l.value=e}return n({updateFolder:p}),{__sfc:!0,folder:l,note:d,user:c,updateFolder:p,t:i.t,NcNoteCard:s.A,NcUserBubble:a.N}}});var d=n(85072),c=n.n(d),p=n(97825),u=n.n(p),A=n(77659),f=n.n(A),m=n(55056),_=n.n(m),C=n(10540),h=n.n(C),v=n(41113),N=n.n(v),g=n(4532),x={};x.styleTagTransform=N(),x.setAttributes=_(),x.insert=f().bind(null,"head"),x.domAPI=u(),x.insertStyleElement=h(),c()(g.A,x),g.A&&g.A.locals&&g.A.locals;const b=(0,n(14486).A)(l,(function(){var e=this,t=e._self._c,n=e._self._setupProxy;return n.note.length>0?t(n.NcNoteCard,{staticClass:"note-to-recipient",attrs:{type:"info"}},[n.user?t("p",{staticClass:"note-to-recipient__heading"},[e._v("\n\t\t"+e._s(n.t("files_sharing","Note from"))+"\n\t\t"),t(n.NcUserBubble,{attrs:{user:n.user.id,"display-name":n.user.displayName}})],1):t("p",{staticClass:"note-to-recipient__heading"},[e._v("\n\t\t"+e._s(n.t("files_sharing","Note:"))+"\n\t")]),e._v(" "),t("p",{staticClass:"note-to-recipient__text",domProps:{textContent:e._s(n.note)}})]):e._e()}),[],!1,null,"514f64d7",null).exports}}]);
|
||||
//# sourceMappingURL=9196-9196.js.map?v=a819b92b9042f565c1cf
|
||||
1
dist/9196-9196.js.map
vendored
Normal file
1
dist/9196-9196.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/9196-9196.js.map.license
vendored
Symbolic link
1
dist/9196-9196.js.map.license
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
9196-9196.js.license
|
||||
4
dist/files-init.js
vendored
4
dist/files-init.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-init.js.map
vendored
2
dist/files-init.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-main.js
vendored
4
dist/files-main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-main.js.map
vendored
2
dist/files-main.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files_sharing-init.js
vendored
4
dist/files_sharing-init.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files_sharing-init.js.map
vendored
2
dist/files_sharing-init.js.map
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue