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.
45 lines
2.0 KiB
TypeScript
45 lines
2.0 KiB
TypeScript
/**
|
|
* Installed-app (PWA) display modes and what they break.
|
|
*
|
|
* In an installed web app, window.open(url, "_blank") is handed to the system
|
|
* browser: a separate browsing context that cannot resolve the app's blob:
|
|
* URLs, so the new window opens blank or errors (#865). Callers that would
|
|
* open a blob: URL in a new window ask here first and show the file in-app
|
|
* instead.
|
|
*/
|
|
|
|
import { downloadAllowedQuietly, type DownloadSubject } from "@/lib/download-guard";
|
|
|
|
type StandaloneNavigator = Navigator & { standalone?: boolean };
|
|
|
|
const APP_DISPLAY_MODES = ["standalone", "fullscreen", "window-controls-overlay"] as const;
|
|
|
|
/** Whether the page runs as an installed app rather than in a browser tab. */
|
|
export function isStandaloneDisplayMode(): boolean {
|
|
if (typeof window === "undefined") return false;
|
|
// iOS Safari home-screen apps report navigator.standalone and (on older
|
|
// versions) no display-mode media feature at all.
|
|
if ((window.navigator as StandaloneNavigator).standalone === true) return true;
|
|
if (typeof window.matchMedia !== "function") return false;
|
|
return APP_DISPLAY_MODES.some((mode) => window.matchMedia(`(display-mode: ${mode})`).matches);
|
|
}
|
|
|
|
/** Whether a new window could load this URL. Only blob: URLs in an installed app cannot. */
|
|
export function canOpenUrlInNewWindow(url: string): boolean {
|
|
return !url.startsWith("blob:") || !isStandaloneDisplayMode();
|
|
}
|
|
|
|
/**
|
|
* Opens a preview URL in a new window when that window can load it. Returns
|
|
* false without opening anything when it cannot, so the caller shows the
|
|
* file in-app (or downloads it) instead of leaving the user a dead window.
|
|
*/
|
|
export function openPreviewInNewWindow(url: string, subject?: DownloadSubject): boolean {
|
|
if (!canOpenUrlInNewWindow(url)) return false;
|
|
// A file in a tab of its own can be saved from there: the same device
|
|
// policy as a download. Refused, the caller previews it in the app.
|
|
if (!downloadAllowedQuietly('attachment', subject)) return false;
|
|
window.open(url, "_blank", "noopener,noreferrer");
|
|
return true;
|
|
}
|