Skip to content

Commit

Permalink
Merge pull request #47797 from nextcloud/backport/47692/stable28
Browse files Browse the repository at this point in the history
[stable28] fix(files): Correctly parse external shares for files UI
  • Loading branch information
sorbaugh authored Sep 10, 2024
2 parents 3c2ead7 + 8a77d84 commit 2a1df3b
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 11 deletions.
48 changes: 48 additions & 0 deletions apps/files_sharing/src/services/SharingService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,25 @@ describe('SharingService share to Node mapping', () => {
tags: [window.OC.TAG_FAVORITE],
}

const remoteFile = {
mimetype: 'text/markdown',
mtime: 1688721600,
permissions: 19,
type: 'file',
file_id: 1234,
id: 4,
share_type: Type.SHARE_TYPE_USER,
parent: null,
remote: 'http://exampe.com',
remote_id: '12345',
share_token: 'share-token',
name: '/test.md',
mountpoint: '/shares/test.md',
owner: 'owner-uid',
user: 'sharee-uid',
accepted: true,
}

test('File', async () => {
jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({
data: {
Expand Down Expand Up @@ -346,6 +365,35 @@ describe('SharingService share to Node mapping', () => {
expect(folder.attributes.favorite).toBe(1)
})

test('Remote file', async () => {
jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({
data: {
ocs: {
data: [remoteFile],
},
},
}))

const shares = await getContents(false, true, false, false)

expect(axios.get).toHaveBeenCalledTimes(1)
expect(shares.contents).toHaveLength(1)

const file = shares.contents[0] as File
expect(file).toBeInstanceOf(File)
expect(file.fileid).toBe(1234)
expect(file.source).toBe('http://localhost/remote.php/dav/files/test/shares/test.md')
expect(file.owner).toBe('owner-uid')
expect(file.mime).toBe('text/markdown')
expect(file.mtime?.getTime()).toBe(remoteFile.mtime * 1000)
// not available for remote shares
expect(file.size).toBe(undefined)
expect(file.permissions).toBe(0)
expect(file.root).toBe('/files/test')
expect(file.attributes).toBeInstanceOf(Object)
expect(file.attributes.favorite).toBe(0)
})

test('Empty', async () => {
jest.spyOn(logger, 'error').mockImplementationOnce(() => {})
jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({
Expand Down
25 changes: 17 additions & 8 deletions apps/files_sharing/src/services/SharingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | nu
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'
if (!ocsEntry.mimetype) {
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.type || (ocsEntry.mimetype ? 'file' : 'folder')

// different naming for remote shares
ocsEntry.item_mtime = ocsEntry.mtime
ocsEntry.file_target = ocsEntry.file_target || ocsEntry.mountpoint

// Need to set permissions to NONE for federated shares
ocsEntry.item_permissions = Permission.NONE
Expand All @@ -70,14 +76,15 @@ const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | nu

// 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
// local shares (this server) use `file_source`, but remote shares (federated) use `file_id`
const fileid = ocsEntry.file_source || ocsEntry.file_id || ocsEntry.id

// Generate path and strip double slashes
const path = ocsEntry?.path || ocsEntry.file_target || ocsEntry.name
const path = ocsEntry.path || ocsEntry.file_target || ocsEntry.name
const source = generateRemoteUrl(`dav/${rootPath}/${path}`.replaceAll(/\/\//gm, '/'))

let mtime = ocsEntry.item_mtime ? new Date((ocsEntry.item_mtime) * 1000) : undefined
// Prefer share time if more recent than item mtime
let mtime = ocsEntry?.item_mtime ? new Date((ocsEntry.item_mtime) * 1000) : undefined
if (ocsEntry?.stime > (ocsEntry?.item_mtime || 0)) {
mtime = new Date((ocsEntry.stime) * 1000)
}
Expand Down Expand Up @@ -169,6 +176,8 @@ const getDeletedShares = function(): AxiosPromise<OCSResponse<any>> {
/**
* Group an array of objects (here Nodes) by a key
* and return an array of arrays of them.
* @param nodes Nodes to group
* @param key The attribute to group by
*/
const groupBy = function(nodes: (Folder | File)[], key: string) {
return Object.values(nodes.reduce(function(acc, curr) {
Expand All @@ -178,7 +187,7 @@ const groupBy = function(nodes: (Folder | File)[], key: string) {
}

export const getContents = async (sharedWithYou = true, sharedWithOthers = true, pendingShares = false, deletedshares = false, filterTypes: number[] = []): Promise<ContentsWithRoot> => {
const promises = [] as AxiosPromise<OCSResponse<any>>[]
const promises = [] as AxiosPromise<OCSResponse<unknown>>[]

if (sharedWithYou) {
promises.push(getSharedWithYou(), getRemoteShares())
Expand Down
Loading

0 comments on commit 2a1df3b

Please sign in to comment.