@nextcloud/files 4.0.0-beta.8 → 4.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/dist/actions/fileAction.d.ts +29 -14
- package/dist/actions/fileListAction.d.ts +12 -3
- package/dist/actions/index.d.ts +2 -2
- package/dist/chunks/{folder-Bf-tAYWu.mjs → folder-CeyZUHai.mjs} +57 -31
- package/dist/chunks/folder-CeyZUHai.mjs.map +1 -0
- package/dist/dav/dav.d.ts +2 -2
- package/dist/dav/davPermissions.d.ts +6 -2
- package/dist/dav/davProperties.d.ts +10 -6
- package/dist/dav/index.d.ts +4 -3
- package/dist/dav.mjs +31 -24
- package/dist/dav.mjs.map +1 -1
- package/dist/fileListFilters.d.ts +36 -8
- package/dist/fileListHeaders.d.ts +11 -3
- package/dist/index.mjs +62 -62
- package/dist/index.mjs.map +1 -1
- package/dist/navigation/index.d.ts +3 -3
- package/dist/navigation/navigation.d.ts +5 -2
- package/dist/navigation/view.d.ts +2 -3
- package/dist/newMenu/NewMenu.d.ts +12 -7
- package/dist/node/file.d.ts +3 -3
- package/dist/node/fileType.d.ts +5 -4
- package/dist/node/folder.d.ts +3 -3
- package/dist/node/node.d.ts +15 -15
- package/dist/node/nodeData.d.ts +13 -8
- package/dist/permissions.d.ts +34 -9
- package/dist/sidebar/SidebarAction.d.ts +1 -1
- package/dist/sidebar/SidebarTab.d.ts +24 -8
- package/dist/types.d.ts +1 -1
- package/dist/utils/fileSize.d.ts +3 -3
- package/dist/utils/fileSorting.d.ts +11 -6
- package/dist/utils/filename-validation.d.ts +10 -7
- package/dist/utils/filename.d.ts +3 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/sorting.d.ts +4 -0
- package/package.json +5 -5
- package/dist/chunks/folder-Bf-tAYWu.mjs.map +0 -1
package/dist/dav.mjs
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { getCurrentUser, onRequestTokenUpdate, getRequestToken } from "@nextcloud/auth";
|
|
2
|
-
import { isPublicShare, getSharingToken } from "@nextcloud/sharing/public";
|
|
3
2
|
import { generateRemoteUrl } from "@nextcloud/router";
|
|
3
|
+
import { isPublicShare, getSharingToken } from "@nextcloud/sharing/public";
|
|
4
4
|
import { createClient, getPatcher } from "webdav";
|
|
5
|
-
import { P as Permission, l as logger, c as NodeStatus, a as File, b as Folder } from "./chunks/folder-
|
|
6
|
-
|
|
5
|
+
import { P as Permission, l as logger, c as NodeStatus, a as File, b as Folder } from "./chunks/folder-CeyZUHai.mjs";
|
|
6
|
+
/*!
|
|
7
|
+
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
|
8
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
9
|
+
*/
|
|
10
|
+
function parsePermissions(permString = "") {
|
|
7
11
|
let permissions = Permission.NONE;
|
|
8
12
|
if (!permString) {
|
|
9
13
|
return permissions;
|
|
10
14
|
}
|
|
11
|
-
if (permString.includes("C") || permString.includes("K")) {
|
|
12
|
-
permissions |= Permission.CREATE;
|
|
13
|
-
}
|
|
14
15
|
if (permString.includes("G")) {
|
|
15
16
|
permissions |= Permission.READ;
|
|
16
17
|
}
|
|
17
|
-
if (permString.includes("W")
|
|
18
|
+
if (permString.includes("W")) {
|
|
19
|
+
permissions |= Permission.WRITE;
|
|
20
|
+
}
|
|
21
|
+
if (permString.includes("CK")) {
|
|
22
|
+
permissions |= Permission.CREATE;
|
|
23
|
+
}
|
|
24
|
+
if (permString.includes("NV")) {
|
|
18
25
|
permissions |= Permission.UPDATE;
|
|
19
26
|
}
|
|
20
27
|
if (permString.includes("D")) {
|
|
@@ -24,7 +31,7 @@ const parsePermissions = function(permString = "") {
|
|
|
24
31
|
permissions |= Permission.SHARE;
|
|
25
32
|
}
|
|
26
33
|
return permissions;
|
|
27
|
-
}
|
|
34
|
+
}
|
|
28
35
|
const defaultDavProperties = [
|
|
29
36
|
"d:getcontentlength",
|
|
30
37
|
"d:getcontenttype",
|
|
@@ -51,7 +58,7 @@ const defaultDavNamespaces = {
|
|
|
51
58
|
oc: "http://owncloud.org/ns",
|
|
52
59
|
ocs: "http://open-collaboration-services.org/ns"
|
|
53
60
|
};
|
|
54
|
-
|
|
61
|
+
function registerDavProperty(prop, namespace = { nc: "http://nextcloud.org/ns" }) {
|
|
55
62
|
if (typeof window._nc_dav_properties === "undefined") {
|
|
56
63
|
window._nc_dav_properties = [...defaultDavProperties];
|
|
57
64
|
window._nc_dav_namespaces = { ...defaultDavNamespaces };
|
|
@@ -73,28 +80,28 @@ const registerDavProperty = function(prop, namespace = { nc: "http://nextcloud.o
|
|
|
73
80
|
window._nc_dav_properties.push(prop);
|
|
74
81
|
window._nc_dav_namespaces = namespaces;
|
|
75
82
|
return true;
|
|
76
|
-
}
|
|
77
|
-
|
|
83
|
+
}
|
|
84
|
+
function getDavProperties() {
|
|
78
85
|
if (typeof window._nc_dav_properties === "undefined") {
|
|
79
86
|
window._nc_dav_properties = [...defaultDavProperties];
|
|
80
87
|
}
|
|
81
88
|
return window._nc_dav_properties.map((prop) => `<${prop} />`).join(" ");
|
|
82
|
-
}
|
|
83
|
-
|
|
89
|
+
}
|
|
90
|
+
function getDavNameSpaces() {
|
|
84
91
|
if (typeof window._nc_dav_namespaces === "undefined") {
|
|
85
92
|
window._nc_dav_namespaces = { ...defaultDavNamespaces };
|
|
86
93
|
}
|
|
87
94
|
return Object.keys(window._nc_dav_namespaces).map((ns) => `xmlns:${ns}="${window._nc_dav_namespaces?.[ns]}"`).join(" ");
|
|
88
|
-
}
|
|
89
|
-
|
|
95
|
+
}
|
|
96
|
+
function getDefaultPropfind() {
|
|
90
97
|
return `<?xml version="1.0"?>
|
|
91
98
|
<d:propfind ${getDavNameSpaces()}>
|
|
92
99
|
<d:prop>
|
|
93
100
|
${getDavProperties()}
|
|
94
101
|
</d:prop>
|
|
95
102
|
</d:propfind>`;
|
|
96
|
-
}
|
|
97
|
-
|
|
103
|
+
}
|
|
104
|
+
function getFavoritesReport() {
|
|
98
105
|
return `<?xml version="1.0"?>
|
|
99
106
|
<oc:filter-files ${getDavNameSpaces()}>
|
|
100
107
|
<d:prop>
|
|
@@ -104,8 +111,8 @@ const getFavoritesReport = function() {
|
|
|
104
111
|
<oc:favorite>1</oc:favorite>
|
|
105
112
|
</oc:filter-rules>
|
|
106
113
|
</oc:filter-files>`;
|
|
107
|
-
}
|
|
108
|
-
|
|
114
|
+
}
|
|
115
|
+
function getRecentSearch(lastModified) {
|
|
109
116
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
110
117
|
<d:searchrequest ${getDavNameSpaces()}
|
|
111
118
|
xmlns:ns="https://github.com/icewind1991/SearchDAV/ns">
|
|
@@ -161,7 +168,7 @@ const getRecentSearch = function(lastModified) {
|
|
|
161
168
|
</d:limit>
|
|
162
169
|
</d:basicsearch>
|
|
163
170
|
</d:searchrequest>`;
|
|
164
|
-
}
|
|
171
|
+
}
|
|
165
172
|
function getRootPath() {
|
|
166
173
|
if (isPublicShare()) {
|
|
167
174
|
return `/files/${getSharingToken()}`;
|
|
@@ -177,7 +184,7 @@ function getRemoteURL() {
|
|
|
177
184
|
return url;
|
|
178
185
|
}
|
|
179
186
|
const defaultRemoteURL = getRemoteURL();
|
|
180
|
-
|
|
187
|
+
function getClient(remoteURL = defaultRemoteURL, headers = {}) {
|
|
181
188
|
const client = createClient(remoteURL, { headers });
|
|
182
189
|
function setHeaders(token) {
|
|
183
190
|
client.setHeaders({
|
|
@@ -200,7 +207,7 @@ const getClient = function(remoteURL = defaultRemoteURL, headers = {}) {
|
|
|
200
207
|
return fetch(url, options);
|
|
201
208
|
});
|
|
202
209
|
return client;
|
|
203
|
-
}
|
|
210
|
+
}
|
|
204
211
|
async function getFavoriteNodes(options = {}) {
|
|
205
212
|
const client = options.client ?? getClient();
|
|
206
213
|
const path = options.path ?? "/";
|
|
@@ -217,7 +224,7 @@ async function getFavoriteNodes(options = {}) {
|
|
|
217
224
|
});
|
|
218
225
|
return contentsResponse.data.filter((node) => node.filename !== path).map((result) => resultToNode(result, davRoot));
|
|
219
226
|
}
|
|
220
|
-
|
|
227
|
+
function resultToNode(node, filesRoot = defaultRootPath, remoteURL = defaultRemoteURL) {
|
|
221
228
|
let userId = getCurrentUser()?.uid;
|
|
222
229
|
if (isPublicShare()) {
|
|
223
230
|
userId = userId ?? "anonymous";
|
|
@@ -252,7 +259,7 @@ const resultToNode = function(node, filesRoot = defaultRootPath, remoteURL = def
|
|
|
252
259
|
};
|
|
253
260
|
delete nodeData.attributes?.props;
|
|
254
261
|
return node.type === "file" ? new File(nodeData) : new Folder(nodeData);
|
|
255
|
-
}
|
|
262
|
+
}
|
|
256
263
|
export {
|
|
257
264
|
defaultDavNamespaces,
|
|
258
265
|
defaultDavProperties,
|
package/dist/dav.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dav.mjs","sources":["../lib/dav/davPermissions.ts","../lib/dav/davProperties.ts","../lib/dav/dav.ts"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport { Permission } from '../permissions'\n\n/**\n * Parse the WebDAV permission string to a permission enum\n *\n * @param permString The DAV permission string\n */\nexport const parsePermissions = function(permString = ''): number {\n\tlet permissions = Permission.NONE\n\n\tif (!permString) { return permissions }\n\n\tif (permString.includes('C') || permString.includes('K')) { permissions |= Permission.CREATE }\n\n\tif (permString.includes('G')) { permissions |= Permission.READ }\n\n\tif (permString.includes('W') || permString.includes('N') || permString.includes('V')) { permissions |= Permission.UPDATE }\n\n\tif (permString.includes('D')) { permissions |= Permission.DELETE }\n\n\tif (permString.includes('R')) { permissions |= Permission.SHARE }\n\n\treturn permissions\n}\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport { getCurrentUser } from '@nextcloud/auth'\nimport logger from '../utils/logger'\n\nexport type DavProperty = { [key: string]: string }\n\nexport const defaultDavProperties = [\n\t'd:getcontentlength',\n\t'd:getcontenttype',\n\t'd:getetag',\n\t'd:getlastmodified',\n\t'd:creationdate',\n\t'd:displayname',\n\t'd:quota-available-bytes',\n\t'd:resourcetype',\n\t'nc:has-preview',\n\t'nc:is-encrypted',\n\t'nc:mount-type',\n\t'oc:comments-unread',\n\t'oc:favorite',\n\t'oc:fileid',\n\t'oc:owner-display-name',\n\t'oc:owner-id',\n\t'oc:permissions',\n\t'oc:size',\n]\n\nexport const defaultDavNamespaces = {\n\td: 'DAV:',\n\tnc: 'http://nextcloud.org/ns',\n\toc: 'http://owncloud.org/ns',\n\tocs: 'http://open-collaboration-services.org/ns',\n}\n\n/**\n * Register custom DAV properties\n *\n * Can be used if your app introduces custom DAV properties, so e.g. the files app can make use of it.\n *\n * @param prop The property\n * @param namespace The namespace of the property\n */\nexport const registerDavProperty = function(prop: string, namespace: DavProperty = { nc: 'http://nextcloud.org/ns' }): boolean {\n\tif (typeof window._nc_dav_properties === 'undefined') {\n\t\twindow._nc_dav_properties = [...defaultDavProperties]\n\t\twindow._nc_dav_namespaces = { ...defaultDavNamespaces }\n\t}\n\n\tconst namespaces = { ...window._nc_dav_namespaces, ...namespace }\n\n\t// Check duplicates\n\tif (window._nc_dav_properties.find((search) => search === prop)) {\n\t\tlogger.warn(`${prop} already registered`, { prop })\n\t\treturn false\n\t}\n\n\tif (prop.startsWith('<') || prop.split(':').length !== 2) {\n\t\tlogger.error(`${prop} is not valid. See example: 'oc:fileid'`, { prop })\n\t\treturn false\n\t}\n\n\tconst ns = prop.split(':')[0]\n\tif (!namespaces[ns]) {\n\t\tlogger.error(`${prop} namespace unknown`, { prop, namespaces })\n\t\treturn false\n\t}\n\n\twindow._nc_dav_properties.push(prop)\n\twindow._nc_dav_namespaces = namespaces\n\treturn true\n}\n\n/**\n * Get the registered dav properties\n */\nexport const getDavProperties = function(): string {\n\tif (typeof window._nc_dav_properties === 'undefined') {\n\t\twindow._nc_dav_properties = [...defaultDavProperties]\n\t}\n\n\treturn window._nc_dav_properties.map((prop) => `<${prop} />`).join(' ')\n}\n\n/**\n * Get the registered dav namespaces\n */\nexport const getDavNameSpaces = function(): string {\n\tif (typeof window._nc_dav_namespaces === 'undefined') {\n\t\twindow._nc_dav_namespaces = { ...defaultDavNamespaces }\n\t}\n\n\treturn Object.keys(window._nc_dav_namespaces)\n\t\t.map((ns) => `xmlns:${ns}=\"${window._nc_dav_namespaces?.[ns]}\"`)\n\t\t.join(' ')\n}\n\n/**\n * Get the default PROPFIND request body\n */\nexport const getDefaultPropfind = function(): string {\n\treturn `<?xml version=\"1.0\"?>\n\t\t<d:propfind ${getDavNameSpaces()}>\n\t\t\t<d:prop>\n\t\t\t\t${getDavProperties()}\n\t\t\t</d:prop>\n\t\t</d:propfind>`\n}\n\n/**\n * Get the REPORT body to filter for favorite nodes\n */\nexport const getFavoritesReport = function(): string {\n\treturn `<?xml version=\"1.0\"?>\n\t\t<oc:filter-files ${getDavNameSpaces()}>\n\t\t\t<d:prop>\n\t\t\t\t${getDavProperties()}\n\t\t\t</d:prop>\n\t\t\t<oc:filter-rules>\n\t\t\t\t<oc:favorite>1</oc:favorite>\n\t\t\t</oc:filter-rules>\n\t\t</oc:filter-files>`\n}\n\n/**\n * Get the SEARCH body to search for recently modified files\n *\n * @param lastModified Oldest timestamp to include (Unix timestamp)\n * @example\n * ```ts\n * // SEARCH for recent files need a different DAV endpoint\n * const client = davGetClient(generateRemoteUrl('dav'))\n * // Timestamp of last week\n * const lastWeek = Math.round(Date.now() / 1000) - (60 * 60 * 24 * 7)\n * const contentsResponse = await client.getDirectoryContents(path, {\n * details: true,\n * data: davGetRecentSearch(lastWeek),\n * headers: {\n * method: 'SEARCH',\n * 'Content-Type': 'application/xml; charset=utf-8',\n * },\n * deep: true,\n * }) as ResponseDataDetailed<FileStat[]>\n * ```\n */\nexport const getRecentSearch = function(lastModified: number): string {\n\treturn `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<d:searchrequest ${getDavNameSpaces()}\n\txmlns:ns=\"https://github.com/icewind1991/SearchDAV/ns\">\n\t<d:basicsearch>\n\t\t<d:select>\n\t\t\t<d:prop>\n\t\t\t\t${getDavProperties()}\n\t\t\t</d:prop>\n\t\t</d:select>\n\t\t<d:from>\n\t\t\t<d:scope>\n\t\t\t\t<d:href>/files/${getCurrentUser()?.uid}/</d:href>\n\t\t\t\t<d:depth>infinity</d:depth>\n\t\t\t</d:scope>\n\t\t</d:from>\n\t\t<d:where>\n\t\t\t<d:and>\n\t\t\t\t<d:or>\n\t\t\t\t\t<d:not>\n\t\t\t\t\t\t<d:eq>\n\t\t\t\t\t\t\t<d:prop>\n\t\t\t\t\t\t\t\t<d:getcontenttype/>\n\t\t\t\t\t\t\t</d:prop>\n\t\t\t\t\t\t\t<d:literal>httpd/unix-directory</d:literal>\n\t\t\t\t\t\t</d:eq>\n\t\t\t\t\t</d:not>\n\t\t\t\t\t<d:eq>\n\t\t\t\t\t\t<d:prop>\n\t\t\t\t\t\t\t<oc:size/>\n\t\t\t\t\t\t</d:prop>\n\t\t\t\t\t\t<d:literal>0</d:literal>\n\t\t\t\t\t</d:eq>\n\t\t\t\t</d:or>\n\t\t\t\t<d:gt>\n\t\t\t\t\t<d:prop>\n\t\t\t\t\t\t<d:getlastmodified/>\n\t\t\t\t\t</d:prop>\n\t\t\t\t\t<d:literal>${lastModified}</d:literal>\n\t\t\t\t</d:gt>\n\t\t\t</d:and>\n\t\t</d:where>\n\t\t<d:orderby>\n\t\t\t<d:order>\n\t\t\t\t<d:prop>\n\t\t\t\t\t<d:getlastmodified/>\n\t\t\t\t</d:prop>\n\t\t\t\t<d:descending/>\n\t\t\t</d:order>\n\t\t</d:orderby>\n\t\t<d:limit>\n\t\t\t<d:nresults>100</d:nresults>\n\t\t\t<ns:firstresult>0</ns:firstresult>\n\t\t</d:limit>\n\t</d:basicsearch>\n</d:searchrequest>`\n}\n","/*\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { DAVResultResponseProps, FileStat, ResponseDataDetailed, WebDAVClient } from 'webdav'\nimport type { Node, NodeData } from '../node/index.ts'\n\nimport { getCurrentUser, getRequestToken, onRequestTokenUpdate } from '@nextcloud/auth'\nimport { getSharingToken, isPublicShare } from '@nextcloud/sharing/public'\nimport { generateRemoteUrl } from '@nextcloud/router'\nimport { createClient, getPatcher } from 'webdav'\nimport { parsePermissions } from './davPermissions.ts'\nimport { getFavoritesReport } from './davProperties.ts'\nimport { File, Folder, NodeStatus } from '../node/index.ts'\n\n/**\n * Nextcloud DAV result response\n */\ninterface ResponseProps extends DAVResultResponseProps {\n\tcreationdate: string\n\tpermissions: string\n\tmime: string\n\tfileid: number\n\tsize: number\n\t'owner-id': string | number\n}\n\n/**\n * Get the DAV root path for the current user or public share\n */\nexport function getRootPath(): string {\n\tif (isPublicShare()) {\n\t\treturn `/files/${getSharingToken()}`\n\t}\n\treturn `/files/${getCurrentUser()?.uid}`\n}\n\n/**\n * The DAV root path for the current user\n * This is a cached version of `getRemoteURL`\n */\nexport const defaultRootPath = getRootPath()\n\n/**\n * Get the DAV remote URL used as base URL for the WebDAV client\n * It also handles public shares\n */\nexport function getRemoteURL(): string {\n\tconst url = generateRemoteUrl('dav')\n\tif (isPublicShare()) {\n\t\treturn url.replace('remote.php', 'public.php')\n\t}\n\treturn url\n}\n\n/**\n * The DAV remote URL used as base URL for the WebDAV client\n * This is a cached version of `getRemoteURL`\n */\nexport const defaultRemoteURL = getRemoteURL()\n\n/**\n * Get a WebDAV client configured to include the Nextcloud request token\n *\n * @param remoteURL The DAV server remote URL\n * @param headers Optional additional headers to set for every request\n */\nexport const getClient = function(remoteURL = defaultRemoteURL, headers: Record<string, string> = {}) {\n\tconst client = createClient(remoteURL, { headers })\n\n\t/**\n\t * Set headers for DAV requests\n\t * @param token CSRF token\n\t */\n\tfunction setHeaders(token: string | null) {\n\t\tclient.setHeaders({\n\t\t\t...headers,\n\t\t\t// Add this so the server knows it is an request from the browser\n\t\t\t'X-Requested-With': 'XMLHttpRequest',\n\t\t\t// Inject user auth\n\t\t\trequesttoken: token ?? '',\n\t\t})\n\t}\n\n\t// refresh headers when request token changes\n\tonRequestTokenUpdate(setHeaders)\n\tsetHeaders(getRequestToken())\n\n\t/**\n\t * Allow to override the METHOD to support dav REPORT\n\t *\n\t * @see https://github.com/perry-mitchell/webdav-client/blob/8d9694613c978ce7404e26a401c39a41f125f87f/source/request.ts\n\t */\n\tconst patcher = getPatcher()\n\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t// @ts-ignore\n\t// https://github.com/perry-mitchell/hot-patcher/issues/6\n\tpatcher.patch('fetch', (url: string, options: RequestInit): Promise<Response> => {\n\t\tconst headers = options.headers as Record<string, string>\n\t\tif (headers?.method) {\n\t\t\toptions.method = headers.method\n\t\t\tdelete headers.method\n\t\t}\n\t\treturn fetch(url, options)\n\t})\n\n\treturn client\n}\n\n/**\n * Use WebDAV to query for favorite Nodes\n *\n * @param options - Options for the favorite query\n * @param options.client - The WebDAV client to use for performing the request\n * @param options.path - Base path for the favorites, if unset all favorites are queried\n * @param options.davRoot - The root path for the DAV user (defaults to `defaultRootPath`)\n * @param options.signal - Optional abort signal to cancel the request\n *\n * @example\n * ```js\n * import { getFavoriteNodes } from '@nextcloud/files'\n *\n * // query favorites for the root\n * const favorites = await getFavoriteNodes()\n * ```\n * @example\n * ```js\n * // Advanced usage with custom client and path\n * import { getClient, defaultRootPath, getFavoriteNodes } from '@nextcloud/files'\n *\n * const client = getClient()\n * const controller = new AbortController()\n * const favoritesPromise = getFavoriteNodes({ client, path: '/some/folder', signal: controller.signal })\n * // you can abort the request if needed\n * controller.abort()\n * // or await the result\n * const favorites = await favoritesPromise\n * ```\n */\nexport async function getFavoriteNodes(options: { client?: WebDAVClient, path?: string, davRoot?: string, signal?: AbortSignal } = {}): Promise<Node[]> {\n\tconst client = options.client ?? getClient()\n\tconst path = options.path ?? '/'\n\tconst davRoot = options.davRoot ?? defaultRootPath\n\n\tconst contentsResponse = await client.getDirectoryContents(`${davRoot}${path}`, {\n\t\tsignal: options.signal,\n\t\tdetails: true,\n\t\tdata: getFavoritesReport(),\n\t\theaders: {\n\t\t\t// see getClient for patched webdav client\n\t\t\tmethod: 'REPORT',\n\t\t},\n\t\tincludeSelf: true,\n\t}) as ResponseDataDetailed<FileStat[]>\n\n\treturn contentsResponse.data\n\t\t.filter(node => node.filename !== path) // exclude current dir\n\t\t.map((result) => resultToNode(result, davRoot))\n}\n\n/**\n * Convert DAV result `FileStat` to `Node`\n *\n * @param node The DAV result\n * @param filesRoot The DAV files root path\n * @param remoteURL The DAV server remote URL (same as on `getClient`)\n */\nexport const resultToNode = function(node: FileStat, filesRoot = defaultRootPath, remoteURL = defaultRemoteURL): Node {\n\tlet userId = getCurrentUser()?.uid\n\tif (isPublicShare()) {\n\t\tuserId = userId ?? 'anonymous'\n\t} else if (!userId) {\n\t\tthrow new Error('No user id found')\n\t}\n\n\tconst props = node.props as ResponseProps\n\tconst permissions = parsePermissions(props?.permissions)\n\tconst owner = String(props?.['owner-id'] || userId)\n\tconst id = props.fileid || 0\n\n\tconst mtime = new Date(Date.parse(node.lastmod))\n\tconst crtime = new Date(Date.parse(props.creationdate))\n\n\tconst nodeData: NodeData = {\n\t\tid,\n\t\tsource: `${remoteURL}${node.filename}`,\n\t\tmtime: !isNaN(mtime.getTime()) && mtime.getTime() !== 0 ? mtime : undefined,\n\t\tcrtime: !isNaN(crtime.getTime()) && crtime.getTime() !== 0 ? crtime : undefined,\n\t\tmime: node.mime || 'application/octet-stream',\n\t\t// Manually cast to work around for https://github.com/perry-mitchell/webdav-client/pull/380\n\t\tdisplayname: props.displayname !== undefined ? String(props.displayname) : undefined,\n\t\tsize: props?.size || Number.parseInt(props.getcontentlength || '0'),\n\t\t// The fileid is set to -1 for failed requests\n\t\tstatus: id < 0 ? NodeStatus.FAILED : undefined,\n\t\tpermissions,\n\t\towner,\n\t\troot: filesRoot,\n\t\tattributes: {\n\t\t\t...node,\n\t\t\t...props,\n\t\t\thasPreview: props?.['has-preview'],\n\t\t},\n\t}\n\n\tdelete nodeData.attributes?.props\n\n\treturn node.type === 'file' ? new File(nodeData) : new Folder(nodeData)\n}\n"],"names":["headers"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dav.mjs","sources":["../lib/dav/davPermissions.ts","../lib/dav/davProperties.ts","../lib/dav/dav.ts"],"sourcesContent":["/*!\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport { Permission } from '../permissions.ts'\n\n/**\n * Parse the WebDAV permission string to a permission enum\n *\n * @param permString - The DAV permission string\n */\nexport function parsePermissions(permString = ''): number {\n\tlet permissions = Permission.NONE\n\tif (!permString) {\n\t\treturn permissions\n\t}\n\n\tif (permString.includes('G')) {\n\t\tpermissions |= Permission.READ\n\t}\n\tif (permString.includes('W')) {\n\t\tpermissions |= Permission.WRITE\n\t}\n\tif (permString.includes('CK')) {\n\t\tpermissions |= Permission.CREATE\n\t}\n\tif (permString.includes('NV')) {\n\t\tpermissions |= Permission.UPDATE\n\t}\n\tif (permString.includes('D')) {\n\t\tpermissions |= Permission.DELETE\n\t}\n\tif (permString.includes('R')) {\n\t\tpermissions |= Permission.SHARE\n\t}\n\n\treturn permissions\n}\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport logger from '../utils/logger.ts'\n\nexport type DavProperty = { [key: string]: string }\n\nexport const defaultDavProperties = [\n\t'd:getcontentlength',\n\t'd:getcontenttype',\n\t'd:getetag',\n\t'd:getlastmodified',\n\t'd:creationdate',\n\t'd:displayname',\n\t'd:quota-available-bytes',\n\t'd:resourcetype',\n\t'nc:has-preview',\n\t'nc:is-encrypted',\n\t'nc:mount-type',\n\t'oc:comments-unread',\n\t'oc:favorite',\n\t'oc:fileid',\n\t'oc:owner-display-name',\n\t'oc:owner-id',\n\t'oc:permissions',\n\t'oc:size',\n]\n\nexport const defaultDavNamespaces = {\n\td: 'DAV:',\n\tnc: 'http://nextcloud.org/ns',\n\toc: 'http://owncloud.org/ns',\n\tocs: 'http://open-collaboration-services.org/ns',\n}\n\n/**\n * Register custom DAV properties\n *\n * Can be used if your app introduces custom DAV properties, so e.g. the files app can make use of it.\n *\n * @param prop The property\n * @param namespace The namespace of the property\n */\nexport function registerDavProperty(prop: string, namespace: DavProperty = { nc: 'http://nextcloud.org/ns' }): boolean {\n\tif (typeof window._nc_dav_properties === 'undefined') {\n\t\twindow._nc_dav_properties = [...defaultDavProperties]\n\t\twindow._nc_dav_namespaces = { ...defaultDavNamespaces }\n\t}\n\n\tconst namespaces = { ...window._nc_dav_namespaces, ...namespace }\n\n\t// Check duplicates\n\tif (window._nc_dav_properties.find((search) => search === prop)) {\n\t\tlogger.warn(`${prop} already registered`, { prop })\n\t\treturn false\n\t}\n\n\tif (prop.startsWith('<') || prop.split(':').length !== 2) {\n\t\tlogger.error(`${prop} is not valid. See example: 'oc:fileid'`, { prop })\n\t\treturn false\n\t}\n\n\tconst ns = prop.split(':')[0]\n\tif (!namespaces[ns]) {\n\t\tlogger.error(`${prop} namespace unknown`, { prop, namespaces })\n\t\treturn false\n\t}\n\n\twindow._nc_dav_properties.push(prop)\n\twindow._nc_dav_namespaces = namespaces\n\treturn true\n}\n\n/**\n * Get the registered dav properties\n */\nexport function getDavProperties(): string {\n\tif (typeof window._nc_dav_properties === 'undefined') {\n\t\twindow._nc_dav_properties = [...defaultDavProperties]\n\t}\n\n\treturn window._nc_dav_properties.map((prop) => `<${prop} />`).join(' ')\n}\n\n/**\n * Get the registered dav namespaces\n */\nexport function getDavNameSpaces(): string {\n\tif (typeof window._nc_dav_namespaces === 'undefined') {\n\t\twindow._nc_dav_namespaces = { ...defaultDavNamespaces }\n\t}\n\n\treturn Object.keys(window._nc_dav_namespaces)\n\t\t.map((ns) => `xmlns:${ns}=\"${window._nc_dav_namespaces?.[ns]}\"`)\n\t\t.join(' ')\n}\n\n/**\n * Get the default PROPFIND request body\n */\nexport function getDefaultPropfind(): string {\n\treturn `<?xml version=\"1.0\"?>\n\t\t<d:propfind ${getDavNameSpaces()}>\n\t\t\t<d:prop>\n\t\t\t\t${getDavProperties()}\n\t\t\t</d:prop>\n\t\t</d:propfind>`\n}\n\n/**\n * Get the REPORT body to filter for favorite nodes\n */\nexport function getFavoritesReport(): string {\n\treturn `<?xml version=\"1.0\"?>\n\t\t<oc:filter-files ${getDavNameSpaces()}>\n\t\t\t<d:prop>\n\t\t\t\t${getDavProperties()}\n\t\t\t</d:prop>\n\t\t\t<oc:filter-rules>\n\t\t\t\t<oc:favorite>1</oc:favorite>\n\t\t\t</oc:filter-rules>\n\t\t</oc:filter-files>`\n}\n\n/**\n * Get the SEARCH body to search for recently modified files\n *\n * @param lastModified Oldest timestamp to include (Unix timestamp)\n * @example\n * ```ts\n * // SEARCH for recent files need a different DAV endpoint\n * const client = davGetClient(generateRemoteUrl('dav'))\n * // Timestamp of last week\n * const lastWeek = Math.round(Date.now() / 1000) - (60 * 60 * 24 * 7)\n * const contentsResponse = await client.getDirectoryContents(path, {\n * details: true,\n * data: davGetRecentSearch(lastWeek),\n * headers: {\n * method: 'SEARCH',\n * 'Content-Type': 'application/xml; charset=utf-8',\n * },\n * deep: true,\n * }) as ResponseDataDetailed<FileStat[]>\n * ```\n */\nexport function getRecentSearch(lastModified: number): string {\n\treturn `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<d:searchrequest ${getDavNameSpaces()}\n\txmlns:ns=\"https://github.com/icewind1991/SearchDAV/ns\">\n\t<d:basicsearch>\n\t\t<d:select>\n\t\t\t<d:prop>\n\t\t\t\t${getDavProperties()}\n\t\t\t</d:prop>\n\t\t</d:select>\n\t\t<d:from>\n\t\t\t<d:scope>\n\t\t\t\t<d:href>/files/${getCurrentUser()?.uid}/</d:href>\n\t\t\t\t<d:depth>infinity</d:depth>\n\t\t\t</d:scope>\n\t\t</d:from>\n\t\t<d:where>\n\t\t\t<d:and>\n\t\t\t\t<d:or>\n\t\t\t\t\t<d:not>\n\t\t\t\t\t\t<d:eq>\n\t\t\t\t\t\t\t<d:prop>\n\t\t\t\t\t\t\t\t<d:getcontenttype/>\n\t\t\t\t\t\t\t</d:prop>\n\t\t\t\t\t\t\t<d:literal>httpd/unix-directory</d:literal>\n\t\t\t\t\t\t</d:eq>\n\t\t\t\t\t</d:not>\n\t\t\t\t\t<d:eq>\n\t\t\t\t\t\t<d:prop>\n\t\t\t\t\t\t\t<oc:size/>\n\t\t\t\t\t\t</d:prop>\n\t\t\t\t\t\t<d:literal>0</d:literal>\n\t\t\t\t\t</d:eq>\n\t\t\t\t</d:or>\n\t\t\t\t<d:gt>\n\t\t\t\t\t<d:prop>\n\t\t\t\t\t\t<d:getlastmodified/>\n\t\t\t\t\t</d:prop>\n\t\t\t\t\t<d:literal>${lastModified}</d:literal>\n\t\t\t\t</d:gt>\n\t\t\t</d:and>\n\t\t</d:where>\n\t\t<d:orderby>\n\t\t\t<d:order>\n\t\t\t\t<d:prop>\n\t\t\t\t\t<d:getlastmodified/>\n\t\t\t\t</d:prop>\n\t\t\t\t<d:descending/>\n\t\t\t</d:order>\n\t\t</d:orderby>\n\t\t<d:limit>\n\t\t\t<d:nresults>100</d:nresults>\n\t\t\t<ns:firstresult>0</ns:firstresult>\n\t\t</d:limit>\n\t</d:basicsearch>\n</d:searchrequest>`\n}\n","/*\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { DAVResultResponseProps, FileStat, ResponseDataDetailed, WebDAVClient } from 'webdav'\nimport type { Node, NodeData } from '../node/index.ts'\n\nimport { getCurrentUser, getRequestToken, onRequestTokenUpdate } from '@nextcloud/auth'\nimport { generateRemoteUrl } from '@nextcloud/router'\nimport { getSharingToken, isPublicShare } from '@nextcloud/sharing/public'\nimport { createClient, getPatcher } from 'webdav'\nimport { File, Folder, NodeStatus } from '../node/index.ts'\nimport { parsePermissions } from './davPermissions.ts'\nimport { getFavoritesReport } from './davProperties.ts'\n\n/**\n * Nextcloud DAV result response\n */\ninterface ResponseProps extends DAVResultResponseProps {\n\tcreationdate: string\n\tpermissions: string\n\tmime: string\n\tfileid: number\n\tsize: number\n\t'owner-id': string | number\n}\n\n/**\n * Get the DAV root path for the current user or public share\n */\nexport function getRootPath(): string {\n\tif (isPublicShare()) {\n\t\treturn `/files/${getSharingToken()}`\n\t}\n\treturn `/files/${getCurrentUser()?.uid}`\n}\n\n/**\n * The DAV root path for the current user\n * This is a cached version of `getRemoteURL`\n */\nexport const defaultRootPath = getRootPath()\n\n/**\n * Get the DAV remote URL used as base URL for the WebDAV client\n * It also handles public shares\n */\nexport function getRemoteURL(): string {\n\tconst url = generateRemoteUrl('dav')\n\tif (isPublicShare()) {\n\t\treturn url.replace('remote.php', 'public.php')\n\t}\n\treturn url\n}\n\n/**\n * The DAV remote URL used as base URL for the WebDAV client\n * This is a cached version of `getRemoteURL`\n */\nexport const defaultRemoteURL = getRemoteURL()\n\n/**\n * Get a WebDAV client configured to include the Nextcloud request token\n *\n * @param remoteURL The DAV server remote URL\n * @param headers Optional additional headers to set for every request\n */\nexport function getClient(remoteURL = defaultRemoteURL, headers: Record<string, string> = {}) {\n\tconst client = createClient(remoteURL, { headers })\n\n\t/**\n\t * Set headers for DAV requests\n\t *\n\t * @param token CSRF token\n\t */\n\tfunction setHeaders(token: string | null) {\n\t\tclient.setHeaders({\n\t\t\t...headers,\n\t\t\t// Add this so the server knows it is an request from the browser\n\t\t\t'X-Requested-With': 'XMLHttpRequest',\n\t\t\t// Inject user auth\n\t\t\trequesttoken: token ?? '',\n\t\t})\n\t}\n\n\t// refresh headers when request token changes\n\tonRequestTokenUpdate(setHeaders)\n\tsetHeaders(getRequestToken())\n\n\t/**\n\t * Allow to override the METHOD to support dav REPORT\n\t *\n\t * @see https://github.com/perry-mitchell/webdav-client/blob/8d9694613c978ce7404e26a401c39a41f125f87f/source/request.ts\n\t */\n\tconst patcher = getPatcher()\n\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t// @ts-ignore\n\t// https://github.com/perry-mitchell/hot-patcher/issues/6\n\tpatcher.patch('fetch', (url: string, options: RequestInit): Promise<Response> => {\n\t\tconst headers = options.headers as Record<string, string>\n\t\tif (headers?.method) {\n\t\t\toptions.method = headers.method\n\t\t\tdelete headers.method\n\t\t}\n\t\treturn fetch(url, options)\n\t})\n\n\treturn client\n}\n\n/**\n * Use WebDAV to query for favorite Nodes\n *\n * @param options - Options for the favorite query\n * @param options.client - The WebDAV client to use for performing the request\n * @param options.path - Base path for the favorites, if unset all favorites are queried\n * @param options.davRoot - The root path for the DAV user (defaults to `defaultRootPath`)\n * @param options.signal - Optional abort signal to cancel the request\n *\n * @example\n * ```js\n * import { getFavoriteNodes } from '@nextcloud/files'\n *\n * // query favorites for the root\n * const favorites = await getFavoriteNodes()\n * ```\n * @example\n * ```js\n * // Advanced usage with custom client and path\n * import { getClient, defaultRootPath, getFavoriteNodes } from '@nextcloud/files'\n *\n * const client = getClient()\n * const controller = new AbortController()\n * const favoritesPromise = getFavoriteNodes({ client, path: '/some/folder', signal: controller.signal })\n * // you can abort the request if needed\n * controller.abort()\n * // or await the result\n * const favorites = await favoritesPromise\n * ```\n */\nexport async function getFavoriteNodes(options: { client?: WebDAVClient, path?: string, davRoot?: string, signal?: AbortSignal } = {}): Promise<Node[]> {\n\tconst client = options.client ?? getClient()\n\tconst path = options.path ?? '/'\n\tconst davRoot = options.davRoot ?? defaultRootPath\n\n\tconst contentsResponse = await client.getDirectoryContents(`${davRoot}${path}`, {\n\t\tsignal: options.signal,\n\t\tdetails: true,\n\t\tdata: getFavoritesReport(),\n\t\theaders: {\n\t\t\t// see getClient for patched webdav client\n\t\t\tmethod: 'REPORT',\n\t\t},\n\t\tincludeSelf: true,\n\t}) as ResponseDataDetailed<FileStat[]>\n\n\treturn contentsResponse.data\n\t\t.filter((node) => node.filename !== path) // exclude current dir\n\t\t.map((result) => resultToNode(result, davRoot))\n}\n\n/**\n * Convert DAV result `FileStat` to `Node`\n *\n * @param node The DAV result\n * @param filesRoot The DAV files root path\n * @param remoteURL The DAV server remote URL (same as on `getClient`)\n */\nexport function resultToNode(node: FileStat, filesRoot = defaultRootPath, remoteURL = defaultRemoteURL): Node {\n\tlet userId = getCurrentUser()?.uid\n\tif (isPublicShare()) {\n\t\tuserId = userId ?? 'anonymous'\n\t} else if (!userId) {\n\t\tthrow new Error('No user id found')\n\t}\n\n\tconst props = node.props as ResponseProps\n\tconst permissions = parsePermissions(props?.permissions)\n\tconst owner = String(props?.['owner-id'] || userId)\n\tconst id = props.fileid || 0\n\n\tconst mtime = new Date(Date.parse(node.lastmod))\n\tconst crtime = new Date(Date.parse(props.creationdate))\n\n\tconst nodeData: NodeData = {\n\t\tid,\n\t\tsource: `${remoteURL}${node.filename}`,\n\t\tmtime: !isNaN(mtime.getTime()) && mtime.getTime() !== 0 ? mtime : undefined,\n\t\tcrtime: !isNaN(crtime.getTime()) && crtime.getTime() !== 0 ? crtime : undefined,\n\t\tmime: node.mime || 'application/octet-stream',\n\t\t// Manually cast to work around for https://github.com/perry-mitchell/webdav-client/pull/380\n\t\tdisplayname: props.displayname !== undefined ? String(props.displayname) : undefined,\n\t\tsize: props?.size || Number.parseInt(props.getcontentlength || '0'),\n\t\t// The fileid is set to -1 for failed requests\n\t\tstatus: id < 0 ? NodeStatus.FAILED : undefined,\n\t\tpermissions,\n\t\towner,\n\t\troot: filesRoot,\n\t\tattributes: {\n\t\t\t...node,\n\t\t\t...props,\n\t\t\thasPreview: props?.['has-preview'],\n\t\t},\n\t}\n\n\tdelete nodeData.attributes?.props\n\n\treturn node.type === 'file' ? new File(nodeData) : new Folder(nodeData)\n}\n"],"names":["headers"],"mappings":"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"}
|
|
@@ -39,7 +39,7 @@ interface IFileListFilterEvents {
|
|
|
39
39
|
'update:filter': FilterUpdateEvent;
|
|
40
40
|
'update:chips': FilterUpdateChipsEvent;
|
|
41
41
|
}
|
|
42
|
-
export interface
|
|
42
|
+
export interface IFileListFilterBase extends TypedEventTarget<IFileListFilterEvents> {
|
|
43
43
|
/**
|
|
44
44
|
* Unique ID of this filter
|
|
45
45
|
*/
|
|
@@ -57,21 +57,48 @@ export interface IFileListFilter extends TypedEventTarget<IFileListFilterEvents>
|
|
|
57
57
|
* @return Subset of the `nodes` parameter to show
|
|
58
58
|
*/
|
|
59
59
|
filter(nodes: INode[]): INode[];
|
|
60
|
-
/**
|
|
61
|
-
* If the filter needs a visual element for settings it can provide a function to mount it.
|
|
62
|
-
* @param el The DOM element to mount to
|
|
63
|
-
*/
|
|
64
|
-
mount?(el: HTMLElement): void;
|
|
65
60
|
/**
|
|
66
61
|
* Reset the filter to the initial state.
|
|
67
|
-
*
|
|
62
|
+
*
|
|
68
63
|
* Implementations should make sure,that if they provide chips they need to emit the `update:chips` event.
|
|
64
|
+
* This is called by the files app.
|
|
69
65
|
*
|
|
70
66
|
* @since 3.10.0
|
|
71
67
|
*/
|
|
72
68
|
reset?(): void;
|
|
73
69
|
}
|
|
74
|
-
|
|
70
|
+
/**
|
|
71
|
+
* File list filter interface with UI component
|
|
72
|
+
*
|
|
73
|
+
* Filters with UI must provide additional information for rendering the filter component.
|
|
74
|
+
* The ui will be rendered depending on the view port - either as inline actions or in a dropdown menu.
|
|
75
|
+
*/
|
|
76
|
+
export interface IFileListFilterWithUi extends IFileListFilterBase {
|
|
77
|
+
/**
|
|
78
|
+
* Display name of the filter
|
|
79
|
+
*/
|
|
80
|
+
readonly displayName: string;
|
|
81
|
+
/**
|
|
82
|
+
* Inline SVG icon as string for the filter
|
|
83
|
+
*/
|
|
84
|
+
readonly iconSvgInline: string;
|
|
85
|
+
/**
|
|
86
|
+
* Tag name of the web component to use as filter UI
|
|
87
|
+
*
|
|
88
|
+
* The web component must extend `HTMLElement` and accept a property `filter` of type `IFileListFilterWithUi`
|
|
89
|
+
* (it will receive the filter instance itself).
|
|
90
|
+
*
|
|
91
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
|
|
92
|
+
*/
|
|
93
|
+
readonly tagName: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* File list filter interface
|
|
97
|
+
*
|
|
98
|
+
* Filters can either be simple filters without UI or filters with a UI component.
|
|
99
|
+
*/
|
|
100
|
+
export type IFileListFilter = IFileListFilterBase | IFileListFilterWithUi;
|
|
101
|
+
export declare class FileListFilter extends TypedEventTarget<IFileListFilterEvents> implements IFileListFilterBase {
|
|
75
102
|
id: string;
|
|
76
103
|
order: number;
|
|
77
104
|
constructor(id: string, order?: number);
|
|
@@ -90,6 +117,7 @@ export declare class FileListFilter extends TypedEventTarget<IFileListFilterEven
|
|
|
90
117
|
export declare function registerFileListFilter(filter: IFileListFilter): void;
|
|
91
118
|
/**
|
|
92
119
|
* Remove a registered filter from the file list
|
|
120
|
+
*
|
|
93
121
|
* @param filterId The unique ID of the filter to remove
|
|
94
122
|
*/
|
|
95
123
|
export declare function unregisterFileListFilter(filterId: string): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IFolder } from './node/folder.ts';
|
|
2
1
|
import { IView } from './navigation/view.ts';
|
|
2
|
+
import { IFolder } from './node/folder.ts';
|
|
3
3
|
export interface HeaderData {
|
|
4
4
|
/** Unique ID */
|
|
5
5
|
id: string;
|
|
@@ -22,5 +22,13 @@ export declare class Header {
|
|
|
22
22
|
get updated(): (folder: IFolder, view: IView) => any;
|
|
23
23
|
private validateHeader;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Register a new file list header.
|
|
27
|
+
*
|
|
28
|
+
* @param header - The header to register
|
|
29
|
+
*/
|
|
30
|
+
export declare function registerFileListHeaders(header: Header): void;
|
|
31
|
+
/**
|
|
32
|
+
* Get all currently registered file list headers.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getFileListHeaders(): Header[];
|
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { l as logger, F as FileType } from "./chunks/folder-
|
|
2
|
-
import { a, b, N, c, P } from "./chunks/folder-
|
|
1
|
+
import { l as logger, F as FileType } from "./chunks/folder-CeyZUHai.mjs";
|
|
2
|
+
import { a, b, N, c, P } from "./chunks/folder-CeyZUHai.mjs";
|
|
3
3
|
import { TypedEventTarget } from "typescript-event-target";
|
|
4
4
|
import isSvg from "is-svg";
|
|
5
5
|
import { getCapabilities } from "@nextcloud/capabilities";
|
|
6
6
|
import { extname, basename } from "@nextcloud/paths";
|
|
7
7
|
import { getCanonicalLocale, getLanguage } from "@nextcloud/l10n";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
})(DefaultType || {});
|
|
8
|
+
const DefaultType = Object.freeze({
|
|
9
|
+
DEFAULT: "default",
|
|
10
|
+
HIDDEN: "hidden"
|
|
11
|
+
});
|
|
13
12
|
class FileAction {
|
|
14
13
|
_action;
|
|
15
14
|
constructor(action) {
|
|
@@ -111,7 +110,7 @@ class FileAction {
|
|
|
111
110
|
}
|
|
112
111
|
}
|
|
113
112
|
}
|
|
114
|
-
|
|
113
|
+
function registerFileAction(action) {
|
|
115
114
|
if (typeof window._nc_fileactions === "undefined") {
|
|
116
115
|
window._nc_fileactions = [];
|
|
117
116
|
logger.debug("FileActions initialized");
|
|
@@ -121,14 +120,14 @@ const registerFileAction = function(action) {
|
|
|
121
120
|
return;
|
|
122
121
|
}
|
|
123
122
|
window._nc_fileactions.push(action);
|
|
124
|
-
}
|
|
125
|
-
|
|
123
|
+
}
|
|
124
|
+
function getFileActions() {
|
|
126
125
|
if (typeof window._nc_fileactions === "undefined") {
|
|
127
126
|
window._nc_fileactions = [];
|
|
128
127
|
logger.debug("FileActions initialized");
|
|
129
128
|
}
|
|
130
129
|
return window._nc_fileactions;
|
|
131
|
-
}
|
|
130
|
+
}
|
|
132
131
|
class FileListAction {
|
|
133
132
|
_action;
|
|
134
133
|
constructor(action) {
|
|
@@ -174,7 +173,7 @@ class FileListAction {
|
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
|
-
|
|
176
|
+
function registerFileListAction(action) {
|
|
178
177
|
if (typeof window._nc_filelistactions === "undefined") {
|
|
179
178
|
window._nc_filelistactions = [];
|
|
180
179
|
}
|
|
@@ -183,13 +182,13 @@ const registerFileListAction = (action) => {
|
|
|
183
182
|
return;
|
|
184
183
|
}
|
|
185
184
|
window._nc_filelistactions.push(action);
|
|
186
|
-
}
|
|
187
|
-
|
|
185
|
+
}
|
|
186
|
+
function getFileListActions() {
|
|
188
187
|
if (typeof window._nc_filelistactions === "undefined") {
|
|
189
188
|
window._nc_filelistactions = [];
|
|
190
189
|
}
|
|
191
190
|
return window._nc_filelistactions;
|
|
192
|
-
}
|
|
191
|
+
}
|
|
193
192
|
function getDefaultExportFromCjs(x) {
|
|
194
193
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
195
194
|
}
|
|
@@ -878,7 +877,7 @@ class Header {
|
|
|
878
877
|
}
|
|
879
878
|
}
|
|
880
879
|
}
|
|
881
|
-
|
|
880
|
+
function registerFileListHeaders(header) {
|
|
882
881
|
if (typeof window._nc_filelistheader === "undefined") {
|
|
883
882
|
window._nc_filelistheader = [];
|
|
884
883
|
logger.debug("FileListHeaders initialized");
|
|
@@ -888,14 +887,14 @@ const registerFileListHeaders = function(header) {
|
|
|
888
887
|
return;
|
|
889
888
|
}
|
|
890
889
|
window._nc_filelistheader.push(header);
|
|
891
|
-
}
|
|
892
|
-
|
|
890
|
+
}
|
|
891
|
+
function getFileListHeaders() {
|
|
893
892
|
if (typeof window._nc_filelistheader === "undefined") {
|
|
894
893
|
window._nc_filelistheader = [];
|
|
895
894
|
logger.debug("FileListHeaders initialized");
|
|
896
895
|
}
|
|
897
896
|
return window._nc_filelistheader;
|
|
898
|
-
}
|
|
897
|
+
}
|
|
899
898
|
function checkOptionalProperty(obj, property, type) {
|
|
900
899
|
if (typeof obj[property] !== "undefined") {
|
|
901
900
|
if (type === "array") {
|
|
@@ -1056,6 +1055,7 @@ class Navigation extends TypedEventTarget {
|
|
|
1056
1055
|
_currentView = null;
|
|
1057
1056
|
/**
|
|
1058
1057
|
* Register a new view on the navigation
|
|
1058
|
+
*
|
|
1059
1059
|
* @param view The view to register
|
|
1060
1060
|
* @throws {Error} if a view with the same id is already registered
|
|
1061
1061
|
* @throws {Error} if the registered view is invalid
|
|
@@ -1070,6 +1070,7 @@ class Navigation extends TypedEventTarget {
|
|
|
1070
1070
|
}
|
|
1071
1071
|
/**
|
|
1072
1072
|
* Remove a registered view
|
|
1073
|
+
*
|
|
1073
1074
|
* @param id The id of the view to remove
|
|
1074
1075
|
*/
|
|
1075
1076
|
remove(id) {
|
|
@@ -1112,24 +1113,32 @@ class Navigation extends TypedEventTarget {
|
|
|
1112
1113
|
return this._views;
|
|
1113
1114
|
}
|
|
1114
1115
|
}
|
|
1115
|
-
|
|
1116
|
+
function getNavigation() {
|
|
1116
1117
|
if (typeof window._nc_navigation === "undefined") {
|
|
1117
1118
|
window._nc_navigation = new Navigation();
|
|
1118
1119
|
logger.debug("Navigation service initialized");
|
|
1119
1120
|
}
|
|
1120
1121
|
return window._nc_navigation;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1122
|
+
}
|
|
1123
|
+
const NewMenuEntryCategory = Object.freeze({
|
|
1124
|
+
/**
|
|
1125
|
+
* For actions where the user is intended to upload from their device
|
|
1126
|
+
*/
|
|
1127
|
+
UploadFromDevice: 0,
|
|
1128
|
+
/**
|
|
1129
|
+
* For actions that create new nodes on the server without uploading
|
|
1130
|
+
*/
|
|
1131
|
+
CreateNew: 1,
|
|
1132
|
+
/**
|
|
1133
|
+
* For everything not matching the other categories
|
|
1134
|
+
*/
|
|
1135
|
+
Other: 2
|
|
1136
|
+
});
|
|
1128
1137
|
class NewMenu {
|
|
1129
1138
|
_entries = [];
|
|
1130
1139
|
registerEntry(entry) {
|
|
1131
1140
|
this.validateEntry(entry);
|
|
1132
|
-
entry.category = entry.category ??
|
|
1141
|
+
entry.category = entry.category ?? NewMenuEntryCategory.CreateNew;
|
|
1133
1142
|
this._entries.push(entry);
|
|
1134
1143
|
}
|
|
1135
1144
|
unregisterEntry(entry) {
|
|
@@ -1265,7 +1274,7 @@ function validateSidebarTab(tab) {
|
|
|
1265
1274
|
throw new Error("Sidebar tabs need to have the tagName name set");
|
|
1266
1275
|
}
|
|
1267
1276
|
if (!tab.tagName.match(/^[a-z][a-z0-9-_]+$/)) {
|
|
1268
|
-
throw new Error(
|
|
1277
|
+
throw new Error('Sidebar tab "tagName" is invalid');
|
|
1269
1278
|
}
|
|
1270
1279
|
if (!tab.displayName || typeof tab.displayName !== "string") {
|
|
1271
1280
|
throw new Error("Sidebar tabs need to have a name set");
|
|
@@ -1276,8 +1285,11 @@ function validateSidebarTab(tab) {
|
|
|
1276
1285
|
if (typeof tab.order !== "number") {
|
|
1277
1286
|
throw new Error("Sidebar tabs need to have a numeric order set");
|
|
1278
1287
|
}
|
|
1279
|
-
if (typeof tab.enabled !== "function") {
|
|
1280
|
-
throw new Error('Sidebar
|
|
1288
|
+
if (tab.enabled && typeof tab.enabled !== "function") {
|
|
1289
|
+
throw new Error('Sidebar tab "enabled" is not a function');
|
|
1290
|
+
}
|
|
1291
|
+
if (tab.onInit && typeof tab.onInit !== "function") {
|
|
1292
|
+
throw new Error('Sidebar tab "onInit" is not a function');
|
|
1281
1293
|
}
|
|
1282
1294
|
}
|
|
1283
1295
|
class SidebarProxy {
|
|
@@ -1321,12 +1333,11 @@ class SidebarProxy {
|
|
|
1321
1333
|
function getSidebar() {
|
|
1322
1334
|
return new SidebarProxy();
|
|
1323
1335
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
})(InvalidFilenameErrorReason || {});
|
|
1336
|
+
const InvalidFilenameErrorReason = Object.freeze({
|
|
1337
|
+
ReservedName: "reserved name",
|
|
1338
|
+
Character: "character",
|
|
1339
|
+
Extension: "extension"
|
|
1340
|
+
});
|
|
1330
1341
|
class InvalidFilenameError extends Error {
|
|
1331
1342
|
constructor(options) {
|
|
1332
1343
|
super(`Invalid ${options.reason} '${options.segment}' in filename '${options.filename}'`, { cause: options });
|
|
@@ -1355,34 +1366,24 @@ function validateFilename(filename) {
|
|
|
1355
1366
|
const forbiddenCharacters = capabilities.forbidden_filename_characters ?? ["/", "\\"];
|
|
1356
1367
|
for (const character of forbiddenCharacters) {
|
|
1357
1368
|
if (filename.includes(character)) {
|
|
1358
|
-
throw new InvalidFilenameError({ segment: character, reason:
|
|
1369
|
+
throw new InvalidFilenameError({ segment: character, reason: InvalidFilenameErrorReason.Character, filename });
|
|
1359
1370
|
}
|
|
1360
1371
|
}
|
|
1361
1372
|
filename = filename.toLocaleLowerCase();
|
|
1362
1373
|
const forbiddenFilenames = capabilities.forbidden_filenames ?? [".htaccess"];
|
|
1363
1374
|
if (forbiddenFilenames.includes(filename)) {
|
|
1364
|
-
throw new InvalidFilenameError({
|
|
1365
|
-
filename,
|
|
1366
|
-
segment: filename,
|
|
1367
|
-
reason: "reserved name"
|
|
1368
|
-
/* ReservedName */
|
|
1369
|
-
});
|
|
1375
|
+
throw new InvalidFilenameError({ filename, segment: filename, reason: InvalidFilenameErrorReason.ReservedName });
|
|
1370
1376
|
}
|
|
1371
1377
|
const endOfBasename = filename.indexOf(".", 1);
|
|
1372
1378
|
const basename2 = filename.substring(0, endOfBasename === -1 ? void 0 : endOfBasename);
|
|
1373
1379
|
const forbiddenFilenameBasenames = capabilities.forbidden_filename_basenames ?? [];
|
|
1374
1380
|
if (forbiddenFilenameBasenames.includes(basename2)) {
|
|
1375
|
-
throw new InvalidFilenameError({
|
|
1376
|
-
filename,
|
|
1377
|
-
segment: basename2,
|
|
1378
|
-
reason: "reserved name"
|
|
1379
|
-
/* ReservedName */
|
|
1380
|
-
});
|
|
1381
|
+
throw new InvalidFilenameError({ filename, segment: basename2, reason: InvalidFilenameErrorReason.ReservedName });
|
|
1381
1382
|
}
|
|
1382
1383
|
const forbiddenFilenameExtensions = capabilities.forbidden_filename_extensions ?? [];
|
|
1383
1384
|
for (const extension of forbiddenFilenameExtensions) {
|
|
1384
1385
|
if (filename.length > extension.length && filename.endsWith(extension)) {
|
|
1385
|
-
throw new InvalidFilenameError({ segment: extension, reason:
|
|
1386
|
+
throw new InvalidFilenameError({ segment: extension, reason: InvalidFilenameErrorReason.Extension, filename });
|
|
1386
1387
|
}
|
|
1387
1388
|
}
|
|
1388
1389
|
}
|
|
@@ -1436,7 +1437,7 @@ function formatFileSize(size, skipSmallSizes = false, binaryPrefixes = false, ba
|
|
|
1436
1437
|
function parseFileSize(value, forceBinary = false) {
|
|
1437
1438
|
try {
|
|
1438
1439
|
value = `${value}`.toLocaleLowerCase().replaceAll(/\s+/g, "").replaceAll(",", ".");
|
|
1439
|
-
} catch
|
|
1440
|
+
} catch {
|
|
1440
1441
|
return null;
|
|
1441
1442
|
}
|
|
1442
1443
|
const match = value.match(/^([0-9]*(\.[0-9]*)?)([kmgtp]?)(i?)b?$/);
|
|
@@ -1484,16 +1485,15 @@ function orderBy(collection, identifiers2, orders) {
|
|
|
1484
1485
|
return 0;
|
|
1485
1486
|
});
|
|
1486
1487
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
})(FilesSortingMode || {});
|
|
1488
|
+
const FilesSortingMode = Object.freeze({
|
|
1489
|
+
Name: "basename",
|
|
1490
|
+
Modified: "mtime",
|
|
1491
|
+
Size: "size"
|
|
1492
|
+
});
|
|
1493
1493
|
function sortNodes(nodes, options = {}) {
|
|
1494
1494
|
const sortingOptions = {
|
|
1495
1495
|
// Default to sort by name
|
|
1496
|
-
sortingMode:
|
|
1496
|
+
sortingMode: FilesSortingMode.Name,
|
|
1497
1497
|
// Default to sort ascending
|
|
1498
1498
|
sortingOrder: "asc",
|
|
1499
1499
|
...options
|
|
@@ -1511,7 +1511,7 @@ function sortNodes(nodes, options = {}) {
|
|
|
1511
1511
|
// 2: Sort folders first if sorting by name
|
|
1512
1512
|
...sortingOptions.sortFoldersFirst ? [(v) => v.type !== "folder"] : [],
|
|
1513
1513
|
// 3: Use sorting mode if NOT basename (to be able to use display name too)
|
|
1514
|
-
...sortingOptions.sortingMode !==
|
|
1514
|
+
...sortingOptions.sortingMode !== FilesSortingMode.Name ? [(v) => v[sortingOptions.sortingMode] ?? v.attributes[sortingOptions.sortingMode]] : [],
|
|
1515
1515
|
// 4: Use display name if available, fallback to name
|
|
1516
1516
|
(v) => basename2(v),
|
|
1517
1517
|
// 5: Finally, use basename if all previous sorting methods failed
|
|
@@ -1523,9 +1523,9 @@ function sortNodes(nodes, options = {}) {
|
|
|
1523
1523
|
// (for 2): always sort folders before files
|
|
1524
1524
|
...sortingOptions.sortFoldersFirst ? ["asc"] : [],
|
|
1525
1525
|
// (for 3): Reverse if sorting by mtime as mtime higher means edited more recent -> lower
|
|
1526
|
-
...sortingOptions.sortingMode ===
|
|
1526
|
+
...sortingOptions.sortingMode === FilesSortingMode.Modified ? [sortingOptions.sortingOrder === "asc" ? "desc" : "asc"] : [],
|
|
1527
1527
|
// (also for 3 so make sure not to conflict with 2 and 3)
|
|
1528
|
-
...sortingOptions.sortingMode !==
|
|
1528
|
+
...sortingOptions.sortingMode !== FilesSortingMode.Modified && sortingOptions.sortingMode !== FilesSortingMode.Name ? [sortingOptions.sortingOrder] : [],
|
|
1529
1529
|
// for 4: use configured sorting direction
|
|
1530
1530
|
sortingOptions.sortingOrder,
|
|
1531
1531
|
// for 5: use configured sorting direction
|