@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/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-Bf-tAYWu.mjs";
6
- const parsePermissions = function(permString = "") {
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") || permString.includes("N") || permString.includes("V")) {
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
- const registerDavProperty = function(prop, namespace = { nc: "http://nextcloud.org/ns" }) {
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
- const getDavProperties = function() {
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
- const getDavNameSpaces = function() {
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
- const getDefaultPropfind = function() {
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
- const getFavoritesReport = function() {
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
- const getRecentSearch = function(lastModified) {
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
- const getClient = function(remoteURL = defaultRemoteURL, headers = {}) {
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
- const resultToNode = function(node, filesRoot = defaultRootPath, remoteURL = defaultRemoteURL) {
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 IFileListFilter extends TypedEventTarget<IFileListFilterEvents> {
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
- * This is called by the files app.
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
- export declare class FileListFilter extends TypedEventTarget<IFileListFilterEvents> implements IFileListFilter {
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
- export declare const registerFileListHeaders: (header: Header) => void;
26
- export declare const getFileListHeaders: () => Header[];
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-Bf-tAYWu.mjs";
2
- import { a, b, N, c, P } from "./chunks/folder-Bf-tAYWu.mjs";
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
- var DefaultType = /* @__PURE__ */ ((DefaultType2) => {
9
- DefaultType2["DEFAULT"] = "default";
10
- DefaultType2["HIDDEN"] = "hidden";
11
- return DefaultType2;
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
- const registerFileAction = function(action) {
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
- const getFileActions = function() {
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
- const registerFileListAction = (action) => {
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
- const getFileListActions = () => {
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
- const registerFileListHeaders = function(header) {
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
- const getFileListHeaders = function() {
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
- const getNavigation = function() {
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
- var NewMenuEntryCategory = /* @__PURE__ */ ((NewMenuEntryCategory2) => {
1123
- NewMenuEntryCategory2[NewMenuEntryCategory2["UploadFromDevice"] = 0] = "UploadFromDevice";
1124
- NewMenuEntryCategory2[NewMenuEntryCategory2["CreateNew"] = 1] = "CreateNew";
1125
- NewMenuEntryCategory2[NewMenuEntryCategory2["Other"] = 2] = "Other";
1126
- return NewMenuEntryCategory2;
1127
- })(NewMenuEntryCategory || {});
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 ?? 1;
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("Sidebar tabs tagName name is invalid");
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 tabs need to have an "enabled" method');
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
- var InvalidFilenameErrorReason = /* @__PURE__ */ ((InvalidFilenameErrorReason2) => {
1325
- InvalidFilenameErrorReason2["ReservedName"] = "reserved name";
1326
- InvalidFilenameErrorReason2["Character"] = "character";
1327
- InvalidFilenameErrorReason2["Extension"] = "extension";
1328
- return InvalidFilenameErrorReason2;
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: "character", filename });
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: "extension", filename });
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 (e) {
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
- var FilesSortingMode = /* @__PURE__ */ ((FilesSortingMode2) => {
1488
- FilesSortingMode2["Name"] = "basename";
1489
- FilesSortingMode2["Modified"] = "mtime";
1490
- FilesSortingMode2["Size"] = "size";
1491
- return FilesSortingMode2;
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: "basename",
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 !== "basename" ? [(v) => v[sortingOptions.sortingMode] ?? v.attributes[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 === "mtime" ? [sortingOptions.sortingOrder === "asc" ? "desc" : "asc"] : [],
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 !== "mtime" && sortingOptions.sortingMode !== "basename" ? [sortingOptions.sortingOrder] : [],
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