diff --git a/modules/opendesk/README.md b/modules/opendesk/README.md deleted file mode 100644 index d313c219b9..0000000000 --- a/modules/opendesk/README.md +++ /dev/null @@ -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. diff --git a/modules/opendesk/element-web/e2e/fixture/app1.png b/modules/opendesk/element-web/e2e/fixture/app1.png deleted file mode 100644 index 2c34416b88..0000000000 Binary files a/modules/opendesk/element-web/e2e/fixture/app1.png and /dev/null differ diff --git a/modules/opendesk/element-web/e2e/fixture/app2.png b/modules/opendesk/element-web/e2e/fixture/app2.png deleted file mode 100644 index ee42954c78..0000000000 Binary files a/modules/opendesk/element-web/e2e/fixture/app2.png and /dev/null differ diff --git a/modules/opendesk/element-web/e2e/fixture/logo.svg b/modules/opendesk/element-web/e2e/fixture/logo.svg deleted file mode 100644 index 90c502cacf..0000000000 --- a/modules/opendesk/element-web/e2e/fixture/logo.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/modules/opendesk/element-web/e2e/fixture/navigation.json b/modules/opendesk/element-web/e2e/fixture/navigation.json deleted file mode 100644 index 79bac98d25..0000000000 --- a/modules/opendesk/element-web/e2e/fixture/navigation.json +++ /dev/null @@ -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" - } - ] - } - ] -} diff --git a/modules/opendesk/element-web/e2e/fixture/silent-login.html b/modules/opendesk/element-web/e2e/fixture/silent-login.html deleted file mode 100644 index ae80633f60..0000000000 --- a/modules/opendesk/element-web/e2e/fixture/silent-login.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Univention Silent Login - - - - diff --git a/modules/opendesk/element-web/e2e/fixture/widget.html b/modules/opendesk/element-web/e2e/fixture/widget.html deleted file mode 100644 index 3e6a834a73..0000000000 --- a/modules/opendesk/element-web/e2e/fixture/widget.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - Demo Widget - - - -

Hello unknown!

- - diff --git a/modules/opendesk/element-web/e2e/opendesk.spec.ts b/modules/opendesk/element-web/e2e/opendesk.spec.ts deleted file mode 100644 index 227f59a3b7..0000000000 --- a/modules/opendesk/element-web/e2e/opendesk.spec.ts +++ /dev/null @@ -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(); - }); -}); diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml deleted file mode 100644 index f6b58ee014..0000000000 --- a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml +++ /dev/null @@ -1,5 +0,0 @@ -- navigation: - - link "Show portal": - - img "Portal logo" - - button "Show menu": - - img diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml deleted file mode 100644 index e979e85507..0000000000 --- a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml +++ /dev/null @@ -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" diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png deleted file mode 100644 index f690835d36..0000000000 Binary files a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png and /dev/null differ diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png deleted file mode 100644 index c1adf808ab..0000000000 Binary files a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png and /dev/null differ diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png deleted file mode 100644 index c1adf808ab..0000000000 Binary files a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png and /dev/null differ diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png deleted file mode 100644 index f79c7ff28b..0000000000 Binary files a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png and /dev/null differ diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json deleted file mode 100644 index d7b37bb152..0000000000 --- a/modules/opendesk/element-web/package.json +++ /dev/null @@ -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" - } -} diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts deleted file mode 100644 index e50aac9ce4..0000000000 --- a/modules/opendesk/element-web/src/index.ts +++ /dev/null @@ -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 { - 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; diff --git a/modules/opendesk/element-web/tsconfig.json b/modules/opendesk/element-web/tsconfig.json deleted file mode 100644 index 8f4f6b8ace..0000000000 --- a/modules/opendesk/element-web/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "lib" - }, - "include": ["src"] -} diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts deleted file mode 100644 index 17dc4d055e..0000000000 --- a/modules/opendesk/element-web/vite.config.ts +++ /dev/null @@ -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" } }, - }, -});