HIGH fixes (7): - H1: VNCdirectory admin i18n — 30+ translation keys added - H2: handleSave try/catch with error toast - H3: Free/busy accountId scoping - H4: cancelEventBookings filter by eventId - H5: Resource picker static apiFetch import - H6: Sharing-store toast messages via lastMessage state - H7: roleLabel for all resource types MEDIUM fixes (11): - M1: identitySignatureMap cleanup on delete - M2: Now-line relative positioning - M3: Radial menu disabled item keyboard nav - M4: Radial menu stable event listener via refs - M5: cancelBooking error on missing booking - M6: PasswordRow isMasked state flag - M7: Extract shared rights into lib/sharing-rights.ts - M8: VNCtalk client server-side guard - M9: Collabora configManager instead of process.env - M10: CONFIG_ENV_MAP VNCdirectory fields - M11: SENSITIVE_CONFIG_KEYS field name unification LOW fixes (7): - L1-L3: Unused imports removed - L4: aria-labels on close, clear, search, spinner - L5-L7: Comments for intentional patterns, null guard
217 lines
5.0 KiB
TypeScript
217 lines
5.0 KiB
TypeScript
import type {
|
|
MailboxRights,
|
|
CalendarRights,
|
|
AddressBookRights,
|
|
FileNodeRights,
|
|
} from "@/lib/jmap/types";
|
|
|
|
export type SharedResourceKind =
|
|
| "mailbox"
|
|
| "calendar"
|
|
| "addressBook"
|
|
| "file";
|
|
|
|
export const MAILBOX_RIGHTS_PRESETS: Record<string, MailboxRights> = {
|
|
read: {
|
|
mayReadItems: true,
|
|
mayAddItems: false,
|
|
mayRemoveItems: false,
|
|
maySetSeen: false,
|
|
maySetKeywords: false,
|
|
mayCreateChild: false,
|
|
mayRename: false,
|
|
mayDelete: false,
|
|
maySubmit: false,
|
|
},
|
|
readWrite: {
|
|
mayReadItems: true,
|
|
mayAddItems: true,
|
|
mayRemoveItems: false,
|
|
maySetSeen: true,
|
|
maySetKeywords: true,
|
|
mayCreateChild: false,
|
|
mayRename: false,
|
|
mayDelete: false,
|
|
maySubmit: true,
|
|
},
|
|
manager: {
|
|
mayReadItems: true,
|
|
mayAddItems: true,
|
|
mayRemoveItems: true,
|
|
maySetSeen: true,
|
|
maySetKeywords: true,
|
|
mayCreateChild: true,
|
|
mayRename: true,
|
|
mayDelete: true,
|
|
maySubmit: true,
|
|
mayShare: true,
|
|
},
|
|
};
|
|
|
|
export const MAILBOX_ROLE_LABELS: Record<string, string> = {
|
|
read: "Viewer",
|
|
readWrite: "Editor",
|
|
manager: "Manager",
|
|
};
|
|
|
|
export const CALENDAR_ROLE_LABELS: Record<string, string> = {
|
|
read: "Viewer",
|
|
readWrite: "Editor",
|
|
manager: "Manager",
|
|
};
|
|
|
|
export const ADDRESSBOOK_ROLE_LABELS: Record<string, string> = {
|
|
read: "Viewer",
|
|
readWrite: "Editor",
|
|
manager: "Manager",
|
|
};
|
|
|
|
export const FILE_ROLE_LABELS: Record<string, string> = {
|
|
read: "Viewer",
|
|
readWrite: "Editor",
|
|
manager: "Manager",
|
|
};
|
|
|
|
export const CALENDAR_RIGHTS_PRESETS: Record<string, CalendarRights> = {
|
|
read: {
|
|
mayReadFreeBusy: true,
|
|
mayReadItems: true,
|
|
mayWriteAll: false,
|
|
mayWriteOwn: false,
|
|
mayUpdatePrivate: false,
|
|
mayRSVP: false,
|
|
mayShare: false,
|
|
mayDelete: false,
|
|
},
|
|
readWrite: {
|
|
mayReadFreeBusy: true,
|
|
mayReadItems: true,
|
|
mayWriteAll: true,
|
|
mayWriteOwn: true,
|
|
mayUpdatePrivate: true,
|
|
mayRSVP: true,
|
|
mayShare: false,
|
|
mayDelete: false,
|
|
},
|
|
manager: {
|
|
mayReadFreeBusy: true,
|
|
mayReadItems: true,
|
|
mayWriteAll: true,
|
|
mayWriteOwn: true,
|
|
mayUpdatePrivate: true,
|
|
mayRSVP: true,
|
|
mayShare: true,
|
|
mayDelete: true,
|
|
},
|
|
};
|
|
|
|
export const ADDRESS_BOOK_RIGHTS_PRESETS: Record<string, AddressBookRights> = {
|
|
read: { mayRead: true, mayWrite: false, mayShare: false, mayDelete: false },
|
|
readWrite: {
|
|
mayRead: true,
|
|
mayWrite: true,
|
|
mayShare: false,
|
|
mayDelete: false,
|
|
},
|
|
manager: {
|
|
mayRead: true,
|
|
mayWrite: true,
|
|
mayShare: true,
|
|
mayDelete: true,
|
|
},
|
|
};
|
|
|
|
export const FILE_RIGHTS_PRESETS: Record<string, FileNodeRights> = {
|
|
read: {
|
|
mayRead: true,
|
|
mayAddChildren: false,
|
|
mayRename: false,
|
|
mayDelete: false,
|
|
mayModifyContent: false,
|
|
mayShare: false,
|
|
},
|
|
readWrite: {
|
|
mayRead: true,
|
|
mayAddChildren: true,
|
|
mayRename: true,
|
|
mayDelete: true,
|
|
mayModifyContent: true,
|
|
mayShare: false,
|
|
},
|
|
manager: {
|
|
mayRead: true,
|
|
mayAddChildren: true,
|
|
mayRename: true,
|
|
mayDelete: true,
|
|
mayModifyContent: true,
|
|
mayShare: true,
|
|
},
|
|
};
|
|
|
|
export function resolveRights(
|
|
kind: SharedResourceKind,
|
|
role: string,
|
|
): MailboxRights | CalendarRights | AddressBookRights | FileNodeRights {
|
|
switch (kind) {
|
|
case "mailbox":
|
|
return (
|
|
MAILBOX_RIGHTS_PRESETS[role] ?? MAILBOX_RIGHTS_PRESETS.read
|
|
);
|
|
case "calendar":
|
|
return (
|
|
CALENDAR_RIGHTS_PRESETS[role] ?? CALENDAR_RIGHTS_PRESETS.read
|
|
);
|
|
case "addressBook":
|
|
return (
|
|
ADDRESS_BOOK_RIGHTS_PRESETS[role] ?? ADDRESS_BOOK_RIGHTS_PRESETS.read
|
|
);
|
|
case "file":
|
|
return FILE_RIGHTS_PRESETS[role] ?? FILE_RIGHTS_PRESETS.read;
|
|
}
|
|
}
|
|
|
|
export function detectMailboxPreset(rights: MailboxRights): string {
|
|
for (const [name, preset] of Object.entries(MAILBOX_RIGHTS_PRESETS)) {
|
|
const keys = Object.keys(preset) as (keyof MailboxRights)[];
|
|
if (
|
|
keys.every(
|
|
(k) =>
|
|
(preset[k] ?? false) === (rights[k] ?? false),
|
|
)
|
|
) {
|
|
return name;
|
|
}
|
|
}
|
|
return "custom";
|
|
}
|
|
|
|
export function detectCalendarPreset(rights: CalendarRights): string {
|
|
for (const [name, preset] of Object.entries(CALENDAR_RIGHTS_PRESETS)) {
|
|
const keys = Object.keys(preset) as (keyof CalendarRights)[];
|
|
if (keys.every((k) => (preset[k] ?? false) === (rights[k] ?? false))) {
|
|
return name;
|
|
}
|
|
}
|
|
return "custom";
|
|
}
|
|
|
|
export function detectAddressBookPreset(rights: AddressBookRights): string {
|
|
for (const [name, preset] of Object.entries(ADDRESS_BOOK_RIGHTS_PRESETS)) {
|
|
const keys = Object.keys(preset) as (keyof AddressBookRights)[];
|
|
if (keys.every((k) => (preset[k] ?? false) === (rights[k] ?? false))) {
|
|
return name;
|
|
}
|
|
}
|
|
return "custom";
|
|
}
|
|
|
|
export function detectFilePreset(rights: FileNodeRights): string {
|
|
for (const [name, preset] of Object.entries(FILE_RIGHTS_PRESETS)) {
|
|
const keys = Object.keys(preset) as (keyof FileNodeRights)[];
|
|
if (keys.every((k) => (preset[k] ?? false) === (rights[k] ?? false))) {
|
|
return name;
|
|
}
|
|
}
|
|
return "custom";
|
|
}
|