Remove opendesk module

This commit is contained in:
Michael Telatynski
2026-06-05 14:30:11 +01:00
parent 31dd8fb928
commit cb8c90c295
18 changed files with 0 additions and 586 deletions
-12
View File
@@ -1,12 +0,0 @@
Example module to load all the nordeck opendesk legacy modules & deprecated customisations as a single modern module.
## Copyright & License
Copyright (c) 2025 New Vector Ltd
This software is multi licensed by New Vector Ltd (Element). It can be used either:
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px"
width="789.322px" height="336.807px" viewBox="0 0 789.322 336.807" enable-background="new 0 0 789.322 336.807"
xml:space="preserve">
<path d="M8.876,7.71v321.386h23.13v7.711H0V0h32.006v7.71H8.876z"/>
<path d="M97.989,109.594v16.264h0.463c4.338-6.191,9.563-10.998,15.684-14.406c6.117-3.402,13.129-5.11,21.027-5.11
c7.588,0,14.521,1.475,20.793,4.415c6.274,2.945,11.038,8.131,14.291,15.567c3.56-5.265,8.4-9.913,14.521-13.94
c6.117-4.025,13.358-6.042,21.724-6.042c6.351,0,12.234,0.776,17.66,2.325c5.418,1.549,10.065,4.027,13.938,7.434
c3.869,3.41,6.889,7.863,9.062,13.357c2.167,5.504,3.253,12.122,3.253,19.869v80.385h-32.993v-68.074
c0-4.025-0.154-7.82-0.465-11.385c-0.313-3.56-1.161-6.656-2.555-9.293c-1.395-2.631-3.45-4.724-6.157-6.274
c-2.711-1.543-6.391-2.322-11.037-2.322s-8.403,0.896-11.269,2.671c-2.868,1.784-5.112,4.109-6.737,6.971
c-1.626,2.869-2.711,6.12-3.252,9.762c-0.545,3.638-0.814,7.318-0.814,11.035v66.91h-32.991v-67.375c0-3.562-0.081-7.087-0.23-10.57
c-0.158-3.487-0.814-6.7-1.978-9.645c-1.162-2.94-3.099-5.304-5.809-7.088c-2.711-1.775-6.699-2.671-11.965-2.671
c-1.551,0-3.603,0.349-6.156,1.048c-2.556,0.697-5.036,2.016-7.435,3.949c-2.404,1.938-4.454,4.726-6.158,8.363
c-1.705,3.642-2.556,8.402-2.556,14.287v69.701h-32.99V109.594H97.989z"/>
<path d="M271.545,127.254c3.405-5.113,7.744-9.215,13.012-12.316c5.264-3.097,11.186-5.303,17.771-6.621
c6.582-1.315,13.205-1.976,19.865-1.976c6.042,0,12.158,0.428,18.354,1.277c6.195,0.855,11.85,2.522,16.962,4.997
c5.111,2.477,9.292,5.926,12.546,10.338c3.253,4.414,4.879,10.262,4.879,17.543v62.494c0,5.428,0.31,10.611,0.931,15.567
c0.615,4.959,1.701,8.676,3.251,11.153h-33.455c-0.621-1.86-1.126-3.755-1.511-5.693c-0.39-1.933-0.661-3.908-0.813-5.923
c-5.267,5.422-11.465,9.217-18.585,11.386c-7.127,2.163-14.407,3.251-21.842,3.251c-5.733,0-11.077-0.698-16.033-2.09
c-4.958-1.395-9.293-3.562-13.01-6.51c-3.718-2.938-6.622-6.656-8.713-11.147s-3.138-9.84-3.138-16.033
c0-6.813,1.199-12.43,3.604-16.84c2.399-4.417,5.495-7.939,9.295-10.575c3.793-2.632,8.129-4.607,13.01-5.923
c4.878-1.315,9.795-2.358,14.752-3.137c4.957-0.772,9.835-1.393,14.638-1.857c4.801-0.466,9.062-1.164,12.779-2.093
c3.718-0.929,6.658-2.282,8.829-4.065c2.165-1.781,3.172-4.375,3.02-7.785c0-3.56-0.58-6.389-1.742-8.479
c-1.161-2.09-2.711-3.719-4.646-4.88c-1.937-1.161-4.183-1.936-6.737-2.325c-2.557-0.382-5.309-0.58-8.248-0.58
c-6.506,0-11.617,1.395-15.335,4.183c-3.716,2.788-5.889,7.437-6.506,13.94h-32.991C266.2,138.793,268.133,132.362,271.545,127.254z
M336.714,173.837c-2.09,0.696-4.337,1.275-6.736,1.741c-2.402,0.465-4.918,0.853-7.551,1.161c-2.635,0.313-5.268,0.698-7.899,1.163
c-2.48,0.461-4.919,1.086-7.317,1.857c-2.404,0.779-4.495,1.822-6.274,3.138c-1.784,1.317-3.216,2.985-4.3,4.994
c-1.085,2.014-1.626,4.571-1.626,7.668c0,2.94,0.541,5.422,1.626,7.431c1.084,2.017,2.558,3.604,4.416,4.765
s4.025,1.976,6.507,2.438c2.475,0.466,5.031,0.698,7.665,0.698c6.505,0,11.537-1.082,15.103-3.253
c3.561-2.166,6.192-4.762,7.899-7.785c1.702-3.019,2.749-6.072,3.137-9.174c0.384-3.097,0.58-5.576,0.58-7.434V170.93
C340.548,172.172,338.806,173.139,336.714,173.837z"/>
<path d="M461.826,109.594v22.072h-24.161v59.479c0,5.573,0.928,9.292,2.788,11.149c1.856,1.859,5.576,2.788,11.152,2.788
c1.859,0,3.638-0.076,5.343-0.232c1.703-0.152,3.33-0.388,4.878-0.696v25.557c-2.788,0.465-5.887,0.773-9.293,0.931
c-3.407,0.149-6.737,0.23-9.99,0.23c-5.111,0-9.953-0.35-14.521-1.048c-4.571-0.695-8.597-2.047-12.081-4.063
c-3.486-2.011-6.236-4.88-8.248-8.597c-2.016-3.714-3.021-8.595-3.021-14.639v-70.859h-19.98v-22.072h19.98V73.582h32.992v36.012
H461.826z"/>
<path d="M508.989,109.594v22.306h0.465c1.546-3.72,3.636-7.163,6.272-10.341c2.634-3.172,5.652-5.885,9.06-8.131
c3.405-2.242,7.047-3.985,10.923-5.228c3.868-1.237,7.898-1.859,12.081-1.859c2.168,0,4.566,0.39,7.202,1.163v30.67
c-1.551-0.312-3.41-0.584-5.576-0.814c-2.17-0.233-4.26-0.35-6.274-0.35c-6.041,0-11.152,1.01-15.332,3.021
c-4.182,2.014-7.55,4.761-10.107,8.247c-2.555,3.487-4.379,7.55-5.462,12.198c-1.083,4.645-1.625,9.682-1.625,15.102v54.133h-32.991
V109.594H508.989z"/>
<path d="M568.931,91.006V63.823h32.994v27.183H568.931z M601.925,109.594v120.117h-32.994V109.594H601.925z"/>
<path d="M619.116,109.594h37.637l21.144,31.365l20.911-31.365h36.476l-39.496,56.226l44.377,63.892h-37.64l-25.093-37.87
l-25.094,37.87H615.4l43.213-63.193L619.116,109.594z"/>
<path d="M780.444,329.096V7.71h-23.13V0h32.008v336.807h-32.008v-7.711H780.444z"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

@@ -1,44 +0,0 @@
{
"categories": [
{
"identifier": "category1",
"display_name": "Category 1",
"entries": [
{
"identifier": "app1",
"display_name": "App 1",
"icon_url": "http://localhost:8080/app1.png",
"link": "https://example.com/app1",
"target": "_blank"
},
{
"identifier": "app2",
"display_name": "App 2",
"icon_url": "http://localhost:8080/app2.png",
"link": "https://example.com/app2",
"target": "_blank"
}
]
},
{
"identifier": "category2",
"display_name": "Category 2",
"entries": [
{
"identifier": "app3",
"display_name": "App 3",
"icon_url": "http://localhost:8080/app1.png",
"link": "https://example.com/app3",
"target": "_blank"
},
{
"identifier": "app4",
"display_name": "App 4",
"icon_url": "http://localhost:8080/app2.png",
"link": "https://example.com/app4",
"target": "_blank"
}
]
}
]
}
@@ -1,14 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Univention Silent Login</title>
<script type="text/javascript">
window.postMessage({
loggedIn: true,
});
</script>
</head>
<body></body>
</html>
@@ -1,52 +0,0 @@
<!--Based on https://github.com/nordeck/element-web-modules/blob/main/e2e/src/deploy/widgets/widget.html-->
<html lang="en">
<head>
<title>Demo Widget</title>
<script>
let sendEventCount = 0;
window.onmessage = async (ev) => {
if (ev.data.action === "capabilities") {
// Return the capabilities for this widget
window.parent.postMessage(
Object.assign(
{
response: {
capabilities: ["org.matrix.msc2762.receive.state_event:m.room.topic"],
},
},
ev.data,
),
"*",
);
} else if (ev.data.action === "notify_capabilities") {
// Ask for an openid token
window.parent.postMessage(
{
api: "fromWidget",
widgetId: ev.data.widgetId,
requestId: "widget-" + sendEventCount,
action: "get_openid",
data: {},
},
"*",
);
} else if (ev.data.action === "get_openid" && ev.data.response?.state === "allowed") {
// Add the userid to the heading
const { access_token } = ev.data.response;
const hsUrl = new URLSearchParams(window.location.search).get("hsUrl");
const response = await fetch(
`${hsUrl}/_matrix/federation/v1/openid/userinfo?access_token=${access_token}`,
);
const { sub } = await response.json();
const titleElement = document.getElementById("title");
titleElement.innerText = `Hello ${sub}!`;
}
};
</script>
</head>
<body>
<h1 id="title">Hello unknown!</h1>
</body>
</html>
@@ -1,217 +0,0 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { rejectToast } from "@element-hq/element-web-playwright-common";
import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js";
import { test, expect } from "../../../../playwright/element-web-test.ts";
declare module "@element-hq/element-web-module-api" {
interface Config {
"net.nordeck.element_web.module.opendesk": {
config: {
banner: {
ics_navigation_json_url: string;
ics_silent_url: string;
portal_logo_svg_url: string;
portal_url: string;
};
custom_css_variables: {
[key: `--cpd-${string}`]: string;
};
};
};
"net.nordeck.element_web.module.widget_toggles": {
config: {
types: string[];
};
};
"net.nordeck.element_web.module.widget_lifecycle": {
widget_permissions: {
[url: string]: {
preload_approved: boolean;
identity_approved: boolean;
capabilities_approved: string[];
};
};
};
}
}
test.use({
displayName: "Timmy",
synapseConfig: async ({ synapseConfig, _homeserver: homeserver }, use) => {
// Required for widget lifecycle test
(homeserver as SynapseContainer).withConfigField("listeners[0].resources[0].names", ["client", "openid"]);
await use(synapseConfig);
},
page: async ({ context, page, moduleDir }, use) => {
for (const path of ["logo.svg", "app1.png", "app2.png"]) {
await context.route(`/${path}*`, async (route) => {
await route.fulfill({ path: `${moduleDir}/e2e/fixture/${path}` });
});
}
await context.route("http://localhost:8080/ics/navigation.json*", async (route) => {
await route.fulfill({
path: `${moduleDir}/e2e/fixture/navigation.json`,
contentType: "application/json",
});
});
await context.route("http://localhost:8080/ics/silent", async (route) => {
await route.fulfill({ path: `${moduleDir}/e2e/fixture/silent-login.html`, contentType: "text/html" });
});
await context.route("http://localhost:8080/widget.html*", async (route) => {
await route.fulfill({ path: `${moduleDir}/e2e/fixture/widget.html`, contentType: "text/html" });
});
await page.goto("/");
await use(page);
},
config: {
"net.nordeck.element_web.module.opendesk": {
config: {
banner: {
ics_navigation_json_url: "http://localhost:8080/ics/navigation.json",
ics_silent_url: "http://localhost:8080/ics/silent",
portal_logo_svg_url: "http://localhost:8080/logo.svg",
portal_url: "https://example.com/portal",
},
custom_css_variables: {
"--cpd-color-text-action-accent": "purple",
},
},
},
"net.nordeck.element_web.module.widget_toggles": {
config: {
types: ["jitsi"],
},
},
"net.nordeck.element_web.module.widget_lifecycle": {
widget_permissions: {
"http://localhost:8080/widget.html": {
preload_approved: true,
identity_approved: true,
capabilities_approved: ["org.matrix.msc2762.receive.state_event:m.room.topic"],
},
},
},
"net.nordeck.element_web.module.guest": {
config: {
guest_user_homeserver_url: "https://matrix.local/",
},
},
},
bypassCSP: true,
launchOptions: {
args: ["--disable-web-security"],
},
});
test.describe("Opendesk", () => {
test("should render top-bar", { tag: ["@screenshot"] }, async ({ page }) => {
await expect(page.getByRole("heading", { name: "Be in your element" })).toBeVisible();
await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal");
const nav = page.locator("nav");
await expect(nav).toMatchScreenshot(`nav_loading.png`);
// The stub silent html doesn't seem to work in Playwright so send the postMessage manually
await page.evaluate(() => {
window.postMessage({
loggedIn: true,
});
});
const trigger = page.getByLabel("Show menu");
await expect(trigger).toBeVisible();
// Assert the banner looks as we expect
await expect(nav).toMatchAriaSnapshot();
await expect(nav).toMatchScreenshot(`nav.png`);
// Check hover styles
await trigger.hover();
await expect(nav).toMatchScreenshot(`nav_hover.png`);
await test.step("open menu", async () => {
await trigger.click();
const app1 = page.getByRole("link", { name: "App 1" });
await expect(app1).toHaveAttribute("href", "https://example.com/app1");
await app1.hover();
// Assert the sidebar looks as we expect
await expect(page.getByTestId("menu-list")).toMatchAriaSnapshot();
await expect(page).toMatchScreenshot(`menu.png`, {
// We exclude this as we don't want to assert Element's styling, only our own
css: `
.mx_AuthPage {
opacity: 0;
}
`,
});
});
await test.step("close menu", async () => {
// Assert it closes by clicking the overlay
await page.getByTestId("menu-backdrop").click();
await expect(page.getByTestId("menu-list")).not.toBeVisible();
});
});
test("should apply custom_css_variables", async ({ page, user }) => {
await expect(page.getByRole("button", { name: "Send a Direct Message" })).toHaveCSS(
"background-color",
"rgb(128, 0, 128)",
); // -> purple
});
test("should show the widget without any permission requests", async ({ page, user, homeserver }) => {
const bot = await homeserver.registerUser("bot", "password", "Bot");
const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>(
"POST",
"/v3/createRoom",
bot.accessToken,
{
name: "Trusted Widget",
},
);
await homeserver.csApi.request<{
event_id: string;
}>("PUT", `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, bot.accessToken, {
id: "1",
creatorUserId: bot.userId,
type: "custom",
name: "Trusted Widget",
url: `http://localhost:8080/widget.html?hsUrl=${encodeURIComponent(homeserver.baseUrl)}`,
});
await homeserver.csApi.request(
"PUT",
`/v3/rooms/${encodeURIComponent(roomId)}/state/io.element.widgets.layout/`,
bot.accessToken,
{
widgets: {
"1": {
container: "top",
},
},
},
);
await homeserver.csApi.request("POST", `/v3/rooms/${encodeURIComponent(roomId)}/invite`, bot.accessToken, {
user_id: user.userId,
});
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await page.getByText("Trusted Widget").click();
await page.getByText("Accept").click();
await expect(
page.frameLocator(`iframe[title="Trusted Widget"]`).getByRole("heading", { name: `Hello ${user.userId}!` }),
).toBeVisible();
});
});
@@ -1,5 +0,0 @@
- navigation:
- link "Show portal":
- img "Portal logo"
- button "Show menu":
- img
@@ -1,15 +0,0 @@
- list:
- listitem:
- text: Category 1
- list:
- listitem:
- link "App 1"
- listitem:
- link "App 2"
- listitem:
- text: Category 2
- list:
- listitem:
- link "App 3"
- listitem:
- link "App 4"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

-32
View File
@@ -1,32 +0,0 @@
{
"name": "nordeck-plugin",
"private": true,
"version": "0.0.0",
"type": "module",
"sideEffects": [
"**/*.css"
],
"license": "SEE LICENSE IN README.md",
"scripts": {
"prepare": "vite build",
"lint:types": "tsc --noEmit",
"lint:codestyle": "echo 'handled by lint:eslint'",
"test": "echo no tests yet"
},
"devDependencies": {
"@element-hq/element-web-module-api": "^1.0.0",
"@nordeck/element-web-guest-module": "^2.0.0",
"@nordeck/element-web-opendesk-module": "^0.5.0",
"@nordeck/element-web-widget-lifecycle-module": "^1.0.1",
"@nordeck/element-web-widget-toggles-module": "^0.1.0",
"@types/node": "^22.10.7",
"@types/react": "^19",
"@vitejs/plugin-react": "^6.0.0",
"react": "19.2.6",
"react-dom": "19.2.6",
"rollup-plugin-external-globals": "^0.13.0",
"typescript": "^6.0.0",
"vite": "^8.0.0",
"vite-plugin-node-polyfills": "^0.28.0"
}
}
-79
View File
@@ -1,79 +0,0 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import NordeckOpendeskModule from "@nordeck/element-web-opendesk-module";
import GuestModule, {
assertValidGuestModuleConfig,
GUEST_MODULE_CONFIG_KEY,
GUEST_MODULE_CONFIG_NAMESPACE,
shouldShowComponent as shouldShowComponentShared,
} from "@nordeck/element-web-guest-module";
import WidgetLifecycleModule from "@nordeck/element-web-widget-lifecycle-module";
import WidgetTogglesModule from "@nordeck/element-web-widget-toggles-module";
import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api";
declare module "@element-hq/element-web-module-api" {
interface Config {
[GUEST_MODULE_CONFIG_NAMESPACE]: {
[GUEST_MODULE_CONFIG_KEY]: {
guest_user_homeserver_url: string;
skip_single_sign_on?: boolean;
guest_user_prefix?: string;
};
};
}
}
declare global {
interface Window {
// XXX: temporary hack until we rewrite everything in modern modules
mxMatrixClientPeg: {
safeGet(): {
getSafeUserId(): string;
};
};
}
}
class OpendeskModule implements Module {
public static readonly moduleApiVersion = "^1.0.0";
public constructor(private api: Api) {}
public async load(): Promise<void> {
const { api } = this;
api._registerLegacyModule(NordeckOpendeskModule);
api._registerLegacyModule(GuestModule);
api._registerLegacyModule(WidgetLifecycleModule);
api._registerLegacyModule(WidgetTogglesModule);
api._registerLegacyComponentVisibilityCustomisations({
// XXX: the following is cribbed out of `@nordeck/element-web-guest-module` due to
// it making imports incompatible with workspaces
/**
* Determines whether or not the active MatrixClient user should be able to use
* the given UI component. If shown, the user might still not be able to use the
* component depending on their contextual permissions. For example, invite options
* might be shown to the user but they won't have permission to invite users to
* the current room: the button will appear disabled.
* @param {UIComponent} component The component to check visibility for.
* @returns {boolean} True (default) if the user is able to see the component, false
* otherwise.
*/
shouldShowComponent(component): boolean {
const config = api.config.get(GUEST_MODULE_CONFIG_NAMESPACE)?.[GUEST_MODULE_CONFIG_KEY] ?? {};
assertValidGuestModuleConfig(config);
const myUserId = window.mxMatrixClientPeg.safeGet().getSafeUserId();
return shouldShowComponentShared(config, myUserId, component);
},
});
}
}
export default OpendeskModule satisfies ModuleFactory;
@@ -1,7 +0,0 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "lib"
},
"include": ["src"]
}
@@ -1,59 +0,0 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig, esmExternalRequirePlugin } from "vite";
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import externalGlobals from "rollup-plugin-external-globals";
const __dirname = dirname(fileURLToPath(import.meta.url));
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, "src/index.ts"),
name: "element-web-plugin-opendesk-nordeck",
fileName: "index",
formats: ["es"],
},
outDir: "lib",
target: "esnext",
sourcemap: true,
rolldownOptions: {
plugins: [
esmExternalRequirePlugin({
external: ["react"],
}),
],
output: {
globals: {
// Reuse React from the host app
react: "window.React",
},
},
},
minify: false,
},
plugins: [
react(),
nodePolyfills({
include: ["events"],
}),
externalGlobals({
// Reuse React from the host app
react: "window.React",
}),
],
define: {
// Use production mode for the build as it is tested against production builds of Element Web,
// this is required for React JSX versions to be compatible.
"process.env.NODE_ENV": "'production'",
"process": { env: { NODE_ENV: "production" } },
},
});