mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-03 00:13:09 +00:00
Organisation > Devices now sets policy.json `organisation.devices`, each
switch off by default and scoped per domain or group:
- No downloads: attachments and files only preview (reader, conversation
view, Pro tab, list chips, .eml previews, the Files app). Download,
Download all, Open in new tab, Save to Files, drag-out, the browser's
PDF viewer (pdf.js instead) and media download controls go away.
- No export: no .eml export, View source, message drag-out, or folder and
account export; a running export stops and its file is deleted when
the switch turns on. Import stays.
- No printing: no Print buttons in mail, calendar and contacts; Ctrl+P
prints a note; the reader frame prints blank.
- No install prompt: no card, and the browser's own banner is held back.
- Watermark: the user's address and the date over the reader,
previews and every printout (mail, calendar, contacts, plugins).
A message follows its own account's policy; what isn't about one message
follows the strictest signed-in account. Every user path hides its
button, and the anchor and new-tab helpers refuse as a backstop
(lib/download-guard.ts), never the blob readers. The plugin APIs
`ui.downloadFile` and `ui.print` refuse too, and the office editor frame
loses `allow-downloads`.
Two assertions in components/mail/__tests__/blob-routing.test.tsx now
expect `{ kind: 'export' }` as the fifth argument of downloadBlob for an
.eml export: the export says what it is, so the backstop checks "No
export" rather than "No downloads" for it.
70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
import { runExport, folderTree, type ExportFolder, type ExportFormat } from "@/lib/mail-export/engine";
|
|
import type { ExportSink } from "@/lib/mail-export/sinks";
|
|
import type { IJMAPClient } from "@/lib/jmap/client-interface";
|
|
import { useAuthStore } from "@/stores/auth-store";
|
|
import { useMailTransferStore } from "@/stores/mail-transfer-store";
|
|
import { downloadAllowed } from "@/lib/download-guard";
|
|
|
|
export interface StartExportInput {
|
|
format: ExportFormat;
|
|
/** JMAP account of the folder (or of the whole export). */
|
|
accountId: string;
|
|
/** One folder (its id in its account); the whole account when absent. */
|
|
mailboxId?: string;
|
|
sink: ExportSink;
|
|
}
|
|
|
|
/** Runs an export as the dialog's job. */
|
|
export function startExport(input: StartExportInput): void {
|
|
const auth = useAuthStore.getState();
|
|
if (!auth.client) return;
|
|
// Backstop for the device policy "No export" (the dialog is not offered then).
|
|
if (!downloadAllowed("export", auth.activeAccountId ?? null)) {
|
|
void input.sink.abort().catch(() => {});
|
|
return;
|
|
}
|
|
const client: IJMAPClient = auth.client;
|
|
const controller = new AbortController();
|
|
let ended: () => void = () => {};
|
|
const finished = new Promise<void>((resolve) => { ended = resolve; });
|
|
useMailTransferStore.getState().startJob({
|
|
kind: "export",
|
|
controller,
|
|
scope: auth.activeAccountId ?? "default",
|
|
finished,
|
|
progress: { phase: "running", exported: 0, failed: 0, failures: [], total: 0, fraction: 0 },
|
|
});
|
|
void (async () => {
|
|
try {
|
|
await run();
|
|
} finally {
|
|
ended();
|
|
}
|
|
})();
|
|
async function run() {
|
|
let folders: ExportFolder[];
|
|
try {
|
|
folders = input.mailboxId
|
|
? [{ mailboxId: input.mailboxId, path: [] }]
|
|
: folderTree(await client.getMailboxes(input.accountId));
|
|
} catch (error) {
|
|
await input.sink.abort().catch(() => {});
|
|
useMailTransferStore.getState().updateProgress({
|
|
phase: "failed", exported: 0, failed: 0, failures: [], total: 0, fraction: 0,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
return;
|
|
}
|
|
const result = await runExport({
|
|
client,
|
|
accountId: input.accountId,
|
|
folders,
|
|
format: input.format,
|
|
sink: input.sink,
|
|
signal: controller.signal,
|
|
onProgress: (progress) => useMailTransferStore.getState().updateProgress(progress),
|
|
});
|
|
useMailTransferStore.getState().updateProgress(result);
|
|
}
|
|
}
|