Skip to content

Commit 956fa35

Browse files
link_utils: exposing whitelisted protocols to user settings
Adding config option to set protocols in the config that are whitelisted to be opened directly. The behaviour is documented in docs\howto\customize-link-protocols.md.
1 parent d270d56 commit 956fa35

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+4853
-6421
lines changed

.github/workflows/node.js.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ jobs:
1010
build:
1111
runs-on: ubuntu-latest
1212
steps:
13+
- uses: actions/setup-node@v4
14+
with:
15+
node-version: lts/*
1316
- uses: actions/checkout@v4
1417
- run: npm ci
1518
- run: npm test

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node-options=--experimental-strip-types

app/common/config-schemata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const configSchemata = {
3636
useManualProxy: z.boolean(),
3737
useProxy: z.boolean(),
3838
useSystemProxy: z.boolean(),
39+
whitelistedProtocols: z.string().array(),
3940
};
4041

4142
export const enterpriseConfigSchemata = {

app/common/config-util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from "node:path";
33

44
import * as Sentry from "@sentry/core";
55
import {JsonDB} from "node-json-db";
6-
import {DataError} from "node-json-db/dist/lib/Errors";
6+
import {DataError} from "node-json-db/dist/lib/Errors.js";
77
import type {z} from "zod";
88
import {app, dialog} from "zulip:remote";
99

app/common/enterprise-util.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ reloadDatabase();
2626
function reloadDatabase(): void {
2727
let enterpriseFile = "/etc/zulip-desktop-config/global_config.json";
2828
if (process.platform === "win32") {
29-
enterpriseFile =
30-
"C:\\Program Files\\Zulip-Desktop-Config\\global_config.json";
29+
enterpriseFile = String.raw`C:\Program Files\Zulip-Desktop-Config\global_config.json`;
3130
}
3231

3332
enterpriseFile = path.resolve(enterpriseFile);

app/common/link-util.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,21 @@ import fs from "node:fs";
33
import os from "node:os";
44
import path from "node:path";
55

6-
import {html} from "./html.ts";
6+
import * as ConfigUtil from "./config-util.ts";
7+
import {Html, html} from "./html.ts";
8+
import * as t from "./translation-util.ts";
9+
10+
/* Fetches the current protocolLaunchers from settings.json */
11+
const whitelistedProtocols = ConfigUtil.getConfigItem("whitelistedProtocols", [
12+
"http:",
13+
"https:",
14+
"mailto:",
15+
"tel:",
16+
"sip:",
17+
]);
718

819
export async function openBrowser(url: URL): Promise<void> {
9-
if (["http:", "https:", "mailto:"].includes(url.protocol)) {
20+
if (whitelistedProtocols.includes(url.protocol)) {
1021
await shell.openExternal(url.href);
1122
} else {
1223
// For security, indirect links to non-whitelisted protocols
@@ -21,15 +32,21 @@ export async function openBrowser(url: URL): Promise<void> {
2132
<head>
2233
<meta charset="UTF-8" />
2334
<meta http-equiv="Refresh" content="0; url=${url.href}" />
24-
<title>Redirecting</title>
35+
<title>${t.__("Redirecting")}</title>
2536
<style>
2637
html {
2738
font-family: menu, "Helvetica Neue", sans-serif;
2839
}
2940
</style>
3041
</head>
3142
<body>
32-
<p>Opening <a href="${url.href}">${url.href}</a></p>
43+
<p>
44+
${new Html({
45+
html: t.__("Opening {{{link}}}…", {
46+
link: html`<a href="${url.href}">${url.href}</a>`.html,
47+
}),
48+
})}
49+
</p>
3350
</body>
3451
</html>
3552
`.html,

app/common/messages.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as t from "./translation-util.ts";
2+
13
type DialogBoxError = {
24
title: string;
35
content: string;
@@ -13,26 +15,24 @@ export function invalidZulipServerError(domain: string): string {
1315
https://zulip.readthedocs.io/en/stable/production/ssl-certificates.html`;
1416
}
1517

16-
export function enterpriseOrgError(
17-
length: number,
18-
domains: string[],
19-
): DialogBoxError {
18+
export function enterpriseOrgError(domains: string[]): DialogBoxError {
2019
let domainList = "";
2120
for (const domain of domains) {
2221
domainList += `• ${domain}\n`;
2322
}
2423

2524
return {
26-
title: `Could not add the following ${
27-
length === 1 ? "organization" : "organizations"
28-
}`,
29-
content: `${domainList}\nPlease contact your system administrator.`,
25+
title: t.__mf(
26+
"{number, plural, one {Could not add # organization} other {Could not add # organizations}}",
27+
{number: domains.length},
28+
),
29+
content: `${domainList}\n${t.__("Please contact your system administrator.")}`,
3030
};
3131
}
3232

3333
export function orgRemovalError(url: string): DialogBoxError {
3434
return {
35-
title: `Removing ${url} is a restricted operation.`,
36-
content: "Please contact your system administrator.",
35+
title: t.__("Removing {{{url}}} is a restricted operation.", {url}),
36+
content: t.__("Please contact your system administrator."),
3737
};
3838
}

app/common/translation-util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ i18n.configure({
1313
/* Fetches the current appLocale from settings.json */
1414
i18n.setLocale(ConfigUtil.getConfigItem("appLanguage", "en") ?? "en");
1515

16-
export {__} from "i18n";
16+
export {__, __mf} from "i18n";

app/main/handle-external-link.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import path from "node:path";
1111

1212
import * as ConfigUtil from "../common/config-util.ts";
1313
import * as LinkUtil from "../common/link-util.ts";
14+
import * as t from "../common/translation-util.ts";
1415

1516
import {send} from "./typed-ipc-main.ts";
1617

@@ -125,8 +126,8 @@ export default function handleExternalLink(
125126
downloadPath,
126127
async completed(filePath: string, fileName: string) {
127128
const downloadNotification = new Notification({
128-
title: "Download Complete",
129-
body: `Click to show ${fileName} in folder`,
129+
title: t.__("Download Complete"),
130+
body: t.__("Click to show {{{fileName}}} in folder", {fileName}),
130131
silent: true, // We'll play our own sound - ding.ogg
131132
});
132133
downloadNotification.on("click", () => {
@@ -149,8 +150,8 @@ export default function handleExternalLink(
149150
if (state !== "cancelled") {
150151
if (ConfigUtil.getConfigItem("promptDownload", false)) {
151152
new Notification({
152-
title: "Download Complete",
153-
body: "Download failed",
153+
title: t.__("Download Complete"),
154+
body: t.__("Download failed"),
154155
}).show();
155156
} else {
156157
contents.downloadURL(url.href);

app/main/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {sentryInit} from "./sentry.ts";
3232
import {setAutoLaunch} from "./startup.ts";
3333
import {ipcMain, send} from "./typed-ipc-main.ts";
3434

35-
import "gatemaker/electron-setup"; // eslint-disable-line import/no-unassigned-import
35+
import "gatemaker/electron-setup.js"; // eslint-disable-line import-x/no-unassigned-import
3636

3737
// eslint-disable-next-line @typescript-eslint/naming-convention
3838
const {GDK_BACKEND} = process.env;
@@ -87,7 +87,7 @@ function createMainWindow(): BrowserWindow {
8787
minWidth: 500,
8888
minHeight: 400,
8989
webPreferences: {
90-
preload: path.join(bundlePath, "renderer.js"),
90+
preload: path.join(bundlePath, "renderer.cjs"),
9191
sandbox: false,
9292
webviewTag: true,
9393
},
@@ -239,9 +239,9 @@ function createMainWindow(): BrowserWindow {
239239
try {
240240
// Check that the data on the clipboard was encrypted to the key.
241241
const data = Buffer.from(clipboard.readText(), "hex");
242-
const iv = data.slice(0, 12);
243-
const ciphertext = data.slice(12, -16);
244-
const authTag = data.slice(-16);
242+
const iv = data.subarray(0, 12);
243+
const ciphertext = data.subarray(12, -16);
244+
const authTag = data.subarray(-16);
245245
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv, {
246246
authTagLength: 16,
247247
});

0 commit comments

Comments
 (0)