diff options
author | skjnldsv <skjnldsv@protonmail.com> | 2024-06-06 18:48:02 +0200 |
---|---|---|
committer | skjnldsv <skjnldsv@protonmail.com> | 2024-06-12 10:27:29 +0200 |
commit | a0a36563b61c85989a0791edfac1ed07bf920787 (patch) | |
tree | fd5b2a47ac2e32e0203edccb1b4f56754c3237c7 /apps/files_sharing/src | |
parent | f626476b11c25ab002e95d2c8ae54fc802bdf185 (diff) | |
download | nextcloud-server-a0a36563b61c85989a0791edfac1ed07bf920787.tar.gz nextcloud-server-a0a36563b61c85989a0791edfac1ed07bf920787.zip |
fix(files_sharing): fix parsing of remote shares
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/files_sharing/src')
-rw-r--r-- | apps/files_sharing/src/actions/sharingStatusAction.ts | 22 | ||||
-rw-r--r-- | apps/files_sharing/src/services/SharingService.spec.ts | 17 | ||||
-rw-r--r-- | apps/files_sharing/src/services/SharingService.ts | 30 |
3 files changed, 53 insertions, 16 deletions
diff --git a/apps/files_sharing/src/actions/sharingStatusAction.ts b/apps/files_sharing/src/actions/sharingStatusAction.ts index 2bcee832b04..f048de1c4fe 100644 --- a/apps/files_sharing/src/actions/sharingStatusAction.ts +++ b/apps/files_sharing/src/actions/sharingStatusAction.ts @@ -17,14 +17,18 @@ import { getCurrentUser } from '@nextcloud/auth' import './sharingStatusAction.scss' -const generateAvatarSvg = (userId: string) => { - const avatarUrl = generateUrl('/avatar/{userId}/32', { userId }) +const generateAvatarSvg = (userId: string, isGuest = false) => { + const avatarUrl = generateUrl(isGuest ? '/avatar/guest/{userId}/32' : '/avatar/{userId}/32?guestFallback=true', { userId }) return `<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" class="sharing-status__avatar"> <image href="${avatarUrl}" height="32" width="32" /> </svg>` } +const isExternal = (node: Node) => { + return node.attributes.remote_id !== undefined +} + export const action = new FileAction({ id: 'sharing-status', displayName(nodes: Node[]) { @@ -33,7 +37,7 @@ export const action = new FileAction({ const ownerId = node?.attributes?.['owner-id'] if (shareTypes.length > 0 - || (ownerId && ownerId !== getCurrentUser()?.uid)) { + || (ownerId !== getCurrentUser()?.uid || isExternal(node))) { return t('files_sharing', 'Shared') } @@ -46,11 +50,11 @@ export const action = new FileAction({ const ownerDisplayName = node?.attributes?.['owner-display-name'] // Mixed share types - if (Array.isArray(node.attributes?.['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) { + if (ownerId && (ownerId !== getCurrentUser()?.uid || isExternal(node))) { return t('files_sharing', 'Shared by {ownerDisplayName}', { ownerDisplayName }) } @@ -62,7 +66,7 @@ export const action = new FileAction({ const shareTypes = Object.values(node?.attributes?.['share-types'] || {}).flat() as number[] // Mixed share types - if (Array.isArray(node.attributes?.['share-types'])) { + if (Array.isArray(node.attributes?.['share-types']) && node.attributes?.['share-types'].length > 1) { return AccountPlusSvg } @@ -84,8 +88,8 @@ export const action = new FileAction({ } const ownerId = node?.attributes?.['owner-id'] - if (ownerId && ownerId !== getCurrentUser()?.uid) { - return generateAvatarSvg(ownerId) + if (ownerId && (ownerId !== getCurrentUser()?.uid || isExternal(node))) { + return generateAvatarSvg(ownerId, isExternal(node)) } return AccountPlusSvg @@ -107,7 +111,7 @@ export const action = new FileAction({ } // If the node is shared by someone else - if (ownerId && ownerId !== getCurrentUser()?.uid) { + if (ownerId && (ownerId !== getCurrentUser()?.uid || isExternal(node))) { return true } diff --git a/apps/files_sharing/src/services/SharingService.spec.ts b/apps/files_sharing/src/services/SharingService.spec.ts index 7786004d49b..d756c4755d7 100644 --- a/apps/files_sharing/src/services/SharingService.spec.ts +++ b/apps/files_sharing/src/services/SharingService.spec.ts @@ -336,12 +336,27 @@ describe('SharingService share to Node mapping', () => { expect(folder.attributes.favorite).toBe(1) }) + test('Empty', async () => { + jest.spyOn(logger, 'error').mockImplementationOnce(() => {}) + jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({ + data: { + ocs: { + data: [], + }, + }, + })) + + const shares = await getContents(false, true, false, false) + expect(shares.contents).toHaveLength(0) + expect(logger.error).toHaveBeenCalledTimes(0) + }) + test('Error', async () => { jest.spyOn(logger, 'error').mockImplementationOnce(() => {}) jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({ data: { ocs: { - data: [{}], + data: [null], }, }, })) diff --git a/apps/files_sharing/src/services/SharingService.ts b/apps/files_sharing/src/services/SharingService.ts index 70e3f8c2555..ad76879257f 100644 --- a/apps/files_sharing/src/services/SharingService.ts +++ b/apps/files_sharing/src/services/SharingService.ts @@ -6,7 +6,7 @@ import type { AxiosPromise } from 'axios' import type { OCSResponse } from '@nextcloud/typings/ocs' -import { Folder, File, type ContentsWithRoot } from '@nextcloud/files' +import { Folder, File, type ContentsWithRoot, Permission } from '@nextcloud/files' import { generateOcsUrl, generateRemoteUrl } from '@nextcloud/router' import { getCurrentUser } from '@nextcloud/auth' import axios from '@nextcloud/axios' @@ -19,16 +19,34 @@ const headers = { 'Content-Type': 'application/json', } -const ocsEntryToNode = function(ocsEntry: any): Folder | File | null { +const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | null> { try { + // Federated share handling + if (ocsEntry?.remote_id !== undefined) { + const mime = (await import('mime')).default + // This won't catch files without an extension, but this is the best we can do + ocsEntry.mimetype = mime.getType(ocsEntry.name) + ocsEntry.item_type = ocsEntry.mimetype ? 'file' : 'folder' + + // Need to set permissions to NONE for federated shares + ocsEntry.item_permissions = Permission.NONE + ocsEntry.permissions = Permission.NONE + + ocsEntry.uid_owner = ocsEntry.owner + // TODO: have the real display name stored somewhere + ocsEntry.displayname_owner = ocsEntry.owner + } + const isFolder = ocsEntry?.item_type === 'folder' const hasPreview = ocsEntry?.has_preview === true const Node = isFolder ? Folder : File - const fileid = ocsEntry.file_source + // If this is an external share that is not yet accepted, + // we don't have an id. We can fallback to the row id temporarily + const fileid = ocsEntry.file_source || ocsEntry.id // Generate path and strip double slashes - const path = ocsEntry?.path || ocsEntry.file_target + const path = ocsEntry?.path || ocsEntry.file_target || ocsEntry.name const source = generateRemoteUrl(`dav/${rootPath}/${path}`.replaceAll(/\/\//gm, '/')) // Prefer share time if more recent than item mtime @@ -41,7 +59,7 @@ const ocsEntryToNode = function(ocsEntry: any): Folder | File | null { id: fileid, source, owner: ocsEntry?.uid_owner, - mime: ocsEntry?.mimetype, + mime: ocsEntry?.mimetype || 'application/octet-stream', mtime, size: ocsEntry?.item_size, permissions: ocsEntry?.item_permissions || ocsEntry?.permissions, @@ -150,7 +168,7 @@ export const getContents = async (sharedWithYou = true, sharedWithOthers = true, const responses = await Promise.all(promises) const data = responses.map((response) => response.data.ocs.data).flat() - let contents = data.map(ocsEntryToNode) + let contents = (await Promise.all(data.map(ocsEntryToNode))) .filter((node) => node !== null) as (Folder | File)[] if (filterTypes.length > 0) { |