Merge pull request #61387 from nextcloud/backport/60151/stable33

[stable33] fix(files_sharing): Hide incompatible actions in shares
This commit is contained in:
Andy Scherzinger 2026-06-17 20:26:24 +02:00 committed by GitHub
commit 2be035be8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 57 additions and 16 deletions

View file

@ -36,6 +36,10 @@ export function registerConvertActions() {
// cannot create the converted file in a public share if we don't have create permissions
return false
}
// Conversion reads the source file, so it requires read permission
if (nodes.some((node) => (node.permissions & Permission.READ) === 0)) {
return false
}
// Check that all nodes have the same mime type
return nodes.every((node) => from === node.mime)
},

View file

@ -535,6 +535,34 @@ describe('SharingService share to Node mapping', () => {
expect(file.attributes.favorite).toBe(0)
})
test('Pending share has no permissions', async () => {
axios.get
.mockReturnValueOnce(Promise.resolve({
data: { ocs: { data: [shareFile] } },
}))
.mockReturnValueOnce(Promise.resolve({
data: { ocs: { data: [] } },
}))
const shares = await getContents(false, false, true, false)
expect(axios.get).toHaveBeenCalledTimes(2)
expect(shares.contents).toHaveLength(1)
expect(shares.contents[0].permissions).toBe(0)
})
test('Deleted share has no permissions', async () => {
axios.get.mockReturnValueOnce(Promise.resolve({
data: { ocs: { data: [shareFolder] } },
}))
const shares = await getContents(false, false, false, true)
expect(axios.get).toHaveBeenCalledTimes(1)
expect(shares.contents).toHaveLength(1)
expect(shares.contents[0].permissions).toBe(0)
})
test('Empty', async () => {
vi.spyOn(logger, 'error').mockImplementationOnce(() => {})
axios.get.mockReturnValueOnce(Promise.resolve({

View file

@ -24,8 +24,9 @@ const headers = {
/**
*
* @param ocsEntry
* @param unmounted whether the share is not mounted into the filesystem (pending or deleted)
*/
async function ocsEntryToNode(ocsEntry: any): Promise<Folder | File | null> {
async function ocsEntryToNode(ocsEntry: any, unmounted = false): Promise<Folder | File | null> {
try {
// Federated share handling
if (ocsEntry?.remote_id !== undefined) {
@ -57,6 +58,13 @@ async function ocsEntryToNode(ocsEntry: any): Promise<Folder | File | null> {
ocsEntry.displayname_owner = ocsEntry.owner
}
// Pending and deleted shares are not mounted into the user's filesystem,
// so no file operation can act on them until they are accepted or restored.
if (unmounted) {
ocsEntry.item_permissions = Permission.NONE
ocsEntry.permissions = Permission.NONE
}
const isFolder = ocsEntry?.item_type === 'folder'
const hasPreview = ocsEntry?.has_preview === true
const Node = isFolder ? Folder : File
@ -238,24 +246,25 @@ function groupBy(nodes: (Folder | File)[], key: string) {
* @param filterTypes
*/
export async function getContents(sharedWithYou = true, sharedWithOthers = true, pendingShares = false, deletedshares = false, filterTypes: number[] = []): Promise<ContentsWithRoot> {
const promises = [] as AxiosPromise<OCSResponse<any>>[]
const requests = [] as { promise: AxiosPromise<OCSResponse<any>>, unmounted: boolean }[]
if (sharedWithYou) {
promises.push(getSharedWithYou(), getRemoteShares())
requests.push({ promise: getSharedWithYou(), unmounted: false }, { promise: getRemoteShares(), unmounted: false })
}
if (sharedWithOthers) {
promises.push(getSharedWithOthers())
requests.push({ promise: getSharedWithOthers(), unmounted: false })
}
if (pendingShares) {
promises.push(getPendingShares(), getRemotePendingShares())
requests.push({ promise: getPendingShares(), unmounted: true }, { promise: getRemotePendingShares(), unmounted: true })
}
if (deletedshares) {
promises.push(getDeletedShares())
requests.push({ promise: getDeletedShares(), unmounted: true })
}
const responses = await Promise.all(promises)
const data = responses.map((response) => response.data.ocs.data).flat()
let contents = (await Promise.all(data.map(ocsEntryToNode)))
const responses = await Promise.all(requests.map(({ promise }) => promise))
const data = responses.flatMap((response, index) => response.data.ocs.data
.map((entry) => ({ entry, unmounted: requests[index].unmounted })))
let contents = (await Promise.all(data.map(({ entry, unmounted }) => ocsEntryToNode(entry, unmounted))))
.filter((node) => node !== null) as (Folder | File)[]
if (filterTypes.length > 0) {

File diff suppressed because one or more lines are too long

4
dist/files-init.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long