/** * @copyright Copyright (c) 2023 John Molakvoæ * * @author John Molakvoæ * * @license AGPL-3.0-or-later * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* eslint-disable camelcase, n/no-extraneous-import */ import type { AxiosPromise } from 'axios' import { Folder, File, type ContentsWithRoot } from '@nextcloud/files' import { generateOcsUrl, generateRemoteUrl } from '@nextcloud/router' import { getCurrentUser } from '@nextcloud/auth' import axios from '@nextcloud/axios' import logger from './logger' export const rootPath = `/files/${getCurrentUser()?.uid}` export type OCSResponse = { ocs: { meta: { status: string statuscode: number message: string }, data: T[] } } const headers = { 'Content-Type': 'application/json', } const ocsEntryToNode = function(ocsEntry: any): Folder | File | null { try { const isFolder = ocsEntry?.item_type === 'folder' const hasPreview = ocsEntry?.has_preview === true const Node = isFolder ? Folder : File const fileid = ocsEntry.file_source // Generate path and strip double slashes const path = ocsEntry?.path || ocsEntry.file_target const source = generateRemoteUrl(`dav/${rootPath}/${path}`.replaceAll(/\/\//gm, '/')) // 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) } return new Node({ id: fileid, source, owner: ocsEntry?.uid_owner, mime: ocsEntry?.mimetype, mtime, size: ocsEntry?.item_size, permissions: ocsEntry?.item_permissions || ocsEntry?.permissions, root: rootPath, attributes: { ...ocsEntry, 'has-preview': hasPreview, // Also check the sharingStatusAction.ts code 'owner-id': ocsEntry?.uid_owner, 'owner-display-name': ocsEntry?.displayname_owner, 'share-types': ocsEntry?.share_type, favorite: ocsEntry?.tags?.includes(window.OC.TAG_FAVORITE) ? 1 : 0, }, }) } catch (error) { logger.error('Error while parsing OCS entry', { error }) return null } } const getShares = function(shared_with_me = false): AxiosPromise> { const url = generateOcsUrl('apps/files_sharing/api/v1/shares') return axios.get(url, { headers, params: { shared_with_me, include_tags: true, }, }) } const getSharedWithYou = function(): AxiosPromise> { return getShares(true) } const getSharedWithOthers = function(): AxiosPromise> { return getShares() } const getRemoteShares = function(): AxiosPromise> { const url = generateOcsUrl('apps/files_sharing/api/v1/remote_shares') return axios.get(url, { headers, params: { include_tags: true, }, }) } const getPendingShares = function(): AxiosPromise> { const url = generateOcsUrl('apps/files_sharing/api/v1/shares/pending') return axios.get(url, { headers, params: { include_tags: true, }, }) } const getRemotePendingShares = function(): AxiosPromise> { const url = generateOcsUrl('apps/files_sharing/api/v1/remote_shares/pending') return axios.get(url, { headers, params: { include_tags: true, }, }) } const getDeletedShares = function(): AxiosPromise> { const url = generateOcsUrl('apps/files_sharing/api/v1/deletedshares') return axios.get(url, { headers, params: { include_tags: true, }, }) } /** * Group an array of objects (here Nodes) by a key * and return an array of arrays of them. */ const groupBy = function(nodes: (Folder | File)[], key: string) { return Object.values(nodes.reduce(function(acc, curr) { (acc[curr[key]] = acc[curr[key]] || []).push(curr) return acc }, {})) as (Folder | File)[][] } export const getContents = async (sharedWithYou = true, sharedWithOthers = true, pendingShares = false, deletedshares = false, filterTypes: number[] = []): Promise => { const promises = [] as AxiosPromise>[] if (sharedWithYou) { promises.push(getSharedWithYou(), getRemoteShares()) } if (sharedWithOthers) { promises.push(getSharedWithOthers()) } if (pendingShares) { promises.push(getPendingShares(), getRemotePendingShares()) } if (deletedshares) { promises.push(getDeletedShares()) } const responses = await Promise.all(promises) const data = responses.map((response) => response.data.ocs.data).flat() let contents = data.map(ocsEntryToNode) .filter((node) => node !== null) as (Folder | File)[] if (filterTypes.length > 0) { contents = contents.filter((node) => filterTypes.includes(node.attributes?.share_type)) } // Merge duplicate shares and group their attributes // Also check the sharingStatusAction.ts code contents = groupBy(contents, 'source').map((nodes) => { const node = nodes[0] node.attributes['share-types'] = nodes.map(node => node.attributes['share-types']) return node }) return { folder: new Folder({ id: 0, source: generateRemoteUrl('dav' + rootPath), owner: getCurrentUser()?.uid || null, }), contents, } }