Skip to content

Commit c8ccff8

Browse files
committed
feat: add separate permission to check for writable files
There is a difference if a file is only updatable (allow to update it means rename or move) or if the file is writable (content can be changed). Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 619323c commit c8ccff8

12 files changed

Lines changed: 132 additions & 73 deletions

File tree

lib/actions/fileAction.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import type { ActionContext, ActionContextSingle } from '../types.ts'
66

77
import logger from '../utils/logger.ts'
88

9-
export enum DefaultType {
10-
DEFAULT = 'default',
11-
HIDDEN = 'hidden',
12-
}
9+
export const DefaultType = Object.freeze({
10+
DEFAULT: 'default',
11+
HIDDEN: 'hidden',
12+
})
13+
14+
export type TDefaultType = typeof DefaultType[keyof typeof DefaultType]
1315

1416
export interface IHotkeyConfig {
1517
/**
@@ -91,14 +93,17 @@ export interface FileActionData {
9193

9294
/**
9395
* Make this action the default.
94-
* If multiple actions are default, the first one
95-
* will be used. The other ones will be put as first
96-
* entries in the actions menu iff DefaultType.Hidden is not used.
97-
* A DefaultType.Hidden action will never be shown
96+
*
97+
* If multiple actions are default, the first one will be used.
98+
* The other ones will be put as first entries in the actions menu iff `DefaultType.Hidden` is not used.
99+
*
100+
* A `DefaultType.Hidden` action will never be shown
98101
* in the actions menu even if another action takes
99102
* its place as default.
103+
*
104+
* @see DefaultType
100105
*/
101-
default?: DefaultType,
106+
default?: TDefaultType,
102107
/**
103108
* If true, the renderInline function will be called
104109
*/

lib/dav/davPermissions.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
1-
/**
1+
/*!
22
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5+
56
import { Permission } from '../permissions'
67

78
/**
89
* Parse the WebDAV permission string to a permission enum
910
*
10-
* @param permString The DAV permission string
11+
* @param permString - The DAV permission string
1112
*/
12-
export const parsePermissions = function(permString = ''): number {
13+
export function parsePermissions(permString = ''): number {
1314
let permissions = Permission.NONE
1415

15-
if (!permString) { return permissions }
16-
17-
if (permString.includes('C') || permString.includes('K')) { permissions |= Permission.CREATE }
18-
19-
if (permString.includes('G')) { permissions |= Permission.READ }
20-
21-
if (permString.includes('W') || permString.includes('N') || permString.includes('V')) { permissions |= Permission.UPDATE }
22-
23-
if (permString.includes('D')) { permissions |= Permission.DELETE }
24-
25-
if (permString.includes('R')) { permissions |= Permission.SHARE }
16+
if (permString.includes('G')) {
17+
permissions |= Permission.READ
18+
}
19+
if (permString.includes('W')) {
20+
permissions |= Permission.WRITE
21+
}
22+
if (permString.includes('CK')) {
23+
permissions |= Permission.CREATE
24+
}
25+
if (permString.includes('NV')) {
26+
permissions |= Permission.UPDATE
27+
}
28+
if (permString.includes('D')) {
29+
permissions |= Permission.DELETE
30+
}
31+
if (permString.includes('R')) {
32+
permissions |= Permission.SHARE
33+
}
2634

2735
return permissions
2836
}

lib/newMenu/NewMenu.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,24 @@ import type { IFolder, INode } from '../node/index.ts'
77

88
import logger from '../utils/logger.ts'
99

10-
export enum NewMenuEntryCategory {
10+
export const NewMenuEntryCategory = Object.freeze({
1111
/**
1212
* For actions where the user is intended to upload from their device
1313
*/
14-
UploadFromDevice = 0,
14+
UploadFromDevice: 0,
1515

1616
/**
1717
* For actions that create new nodes on the server without uploading
1818
*/
19-
CreateNew = 1,
19+
CreateNew: 1,
2020

2121
/**
2222
* For everything not matching the other categories
2323
*/
24-
Other = 2,
25-
}
24+
Other: 2,
25+
})
26+
27+
export type TNewMenuEntryCategory = typeof NewMenuEntryCategory[keyof typeof NewMenuEntryCategory]
2628

2729
export interface NewMenuEntry {
2830
/** Unique ID */
@@ -31,10 +33,12 @@ export interface NewMenuEntry {
3133
/**
3234
* Category to put this entry in
3335
* (supported since Nextcloud 30)
34-
* @since 3.3.0
36+
*
3537
* @default NewMenuEntryCategory.CreateNew
38+
* @see NewMenuEntryCategory
39+
* @since 3.3.0
3640
*/
37-
category?: NewMenuEntryCategory
41+
category?: TNewMenuEntryCategory
3842

3943
/** Translatable string displayed in the menu */
4044
displayName: string

lib/node/file.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5+
56
import type { NodeConstructorData } from './node'
67

78
import { FileType } from './fileType'
@@ -13,7 +14,7 @@ export class File extends Node {
1314
super(data, davService)
1415
}
1516

16-
get type(): FileType.File {
17+
get type(): typeof FileType.File {
1718
return FileType.File
1819
}
1920

lib/node/fileType.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
export enum FileType {
7-
Folder = 'folder',
8-
File = 'file',
9-
}
6+
export const FileType = Object.freeze({
7+
Folder: 'folder',
8+
File: 'file',
9+
})
10+
11+
export type TFileType = typeof FileType[keyof typeof FileType]

lib/node/folder.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5+
56
import type { NodeConstructorData } from './node'
67

78
import { FileType } from './fileType'
@@ -17,7 +18,7 @@ export class Folder extends Node {
1718
}, davService)
1819
}
1920

20-
get type(): FileType.Folder {
21+
get type(): typeof FileType.Folder {
2122
return FileType.Folder
2223
}
2324

lib/node/node.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6+
import type { TFileType } from './fileType.ts'
7+
68
import { basename, dirname, encodePath, extname } from '@nextcloud/paths'
79
import { Permission } from '../permissions'
8-
import { FileType } from './fileType'
910
import { Attribute, fixDates, fixRegExp, isDavResource, NodeData, validateData } from './nodeData'
1011

11-
export enum NodeStatus {
12+
export const NodeStatus = Object.freeze({
1213
/** This is a new node and it doesn't exists on the filesystem yet */
13-
NEW = 'new',
14+
NEW: 'new',
1415
/** This node has failed and is unavailable */
15-
FAILED = 'failed',
16+
FAILED: 'failed',
1617
/** This node is currently loading or have an operation in progress */
17-
LOADING = 'loading',
18+
LOADING: 'loading',
1819
/** This node is locked and cannot be modified */
19-
LOCKED = 'locked',
20-
}
20+
LOCKED: 'locked',
21+
})
22+
23+
export type TNodeStatus = typeof NodeStatus[keyof typeof NodeStatus]
2124

2225
export type NodeConstructorData = [NodeData, RegExp?]
2326

@@ -147,7 +150,7 @@ export abstract class Node {
147150
/**
148151
* Is it a file or a folder ?
149152
*/
150-
abstract get type(): FileType
153+
abstract get type(): TFileType
151154

152155
/**
153156
* Get the file mime
@@ -217,7 +220,7 @@ export abstract class Node {
217220
/**
218221
* Get the file permissions
219222
*/
220-
get permissions(): Permission {
223+
get permissions(): number {
221224
// If this is not a dav resource, we can only read it
222225
if (this.owner === null && !this.isDavResource) {
223226
return Permission.READ
@@ -232,7 +235,7 @@ export abstract class Node {
232235
/**
233236
* Set the file permissions
234237
*/
235-
set permissions(permissions: Permission) {
238+
set permissions(permissions: number) {
236239
validateData({ ...this._data, permissions }, this._knownDavService)
237240
this.updateMtime()
238241
this._data.permissions = permissions
@@ -307,14 +310,14 @@ export abstract class Node {
307310
/**
308311
* Get the node status.
309312
*/
310-
get status(): NodeStatus|undefined {
313+
get status(): TNodeStatus|undefined {
311314
return this._data?.status
312315
}
313316

314317
/**
315318
* Set the node status.
316319
*/
317-
set status(status: NodeStatus|undefined) {
320+
set status(status: TNodeStatus|undefined) {
318321
validateData({ ...this._data, status }, this._knownDavService)
319322
this._data.status = status
320323
}

lib/node/nodeData.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { join } from '@nextcloud/paths'
77

88
import { Permission } from '../permissions'
9-
import { NodeStatus } from './node'
9+
import { NodeStatus, TNodeStatus } from './node'
1010

1111
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1212
export interface Attribute { [key: string]: any }
@@ -41,8 +41,13 @@ export interface NodeData {
4141
/** The node size type */
4242
size?: number
4343

44-
/** The node permissions */
45-
permissions?: Permission
44+
/**
45+
* The node permissions.
46+
*
47+
* A binary mask of `Permission` values.
48+
* @see Permission
49+
*/
50+
permissions?: number
4651

4752
/** The owner UID of this node */
4853
owner: string|null
@@ -54,7 +59,7 @@ export interface NodeData {
5459
attributes?: Attribute
5560

5661
/** The node status */
57-
status?: NodeStatus
62+
status?: TNodeStatus
5863
}
5964

6065
/**

lib/permissions.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,37 @@
66
/**
77
* Node permissions
88
*/
9-
export enum Permission {
10-
NONE = 0,
11-
CREATE = 4,
12-
READ = 1,
13-
UPDATE = 2,
14-
DELETE = 8,
15-
SHARE = 16,
16-
ALL = 31,
17-
}
9+
export const Permission = Object.freeze({
10+
/**
11+
* No permissions granted
12+
*/
13+
NONE: 0,
14+
/**
15+
* Can read the file content
16+
*/
17+
READ: 1,
18+
/**
19+
* Can modify the file itself (move, rename, etc)
20+
*/
21+
UPDATE: 2,
22+
/**
23+
* Can create new files/folders inside a folder
24+
*/
25+
CREATE: 4,
26+
/**
27+
* Can change the file content
28+
*/
29+
WRITE: 4,
30+
/**
31+
* Can delete the node
32+
*/
33+
DELETE: 8,
34+
/**
35+
* Can share the node
36+
*/
37+
SHARE: 16,
38+
/**
39+
* All permissions are granted
40+
*/
41+
ALL: 31,
42+
})

lib/utils/fileSorting.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ import type { SortingOrder } from './sorting.ts'
99
import { FileType } from '../node/fileType.ts'
1010
import { orderBy } from './sorting.ts'
1111

12-
export enum FilesSortingMode {
13-
Name = 'basename',
14-
Modified = 'mtime',
15-
Size = 'size',
16-
}
12+
export const FilesSortingMode = Object.freeze({
13+
Name: 'basename',
14+
Modified: 'mtime',
15+
Size: 'size',
16+
})
17+
18+
export type TFilesSortingMode = typeof FilesSortingMode[keyof typeof FilesSortingMode]
1719

1820
export interface FilesSortingOptions {
1921
/**
2022
* They key to order the files by
2123
* @default FilesSortingMode.Name
2224
*/
23-
sortingMode?: FilesSortingMode | string
25+
sortingMode?: TFilesSortingMode | string
2426

2527
/**
2628
* @default 'asc'

0 commit comments

Comments
 (0)