diff --git a/docs/maps/meeting-rooms.md b/docs/maps/meeting-rooms.md index be86bda0..f89a0443 100644 --- a/docs/maps/meeting-rooms.md +++ b/docs/maps/meeting-rooms.md @@ -11,7 +11,7 @@ In order to create Jitsi meet zones: * You must create a specific object. * Object must be of type "`area`" -* In object properties, you MUST add a "`jitsiRoom`" property (of type "`string`"). The value of the property is the name of the room in Jitsi. Note: the name of the room will be "slugified" and prepended with the name of the instance of the map (so that different instances of the map have different rooms) +* In object properties, you MUST add a "`jitsiRoom`" property (of type "`string`"). The value of the property is the name of the room in Jitsi. Note: the name of the room will be "slugified" and prepended with a hash of the room URL * You may also use "jitsiWidth" property (of type "number" between 0 and 100) to control the width of the iframe containing the meeting room. You can have this object (i.e. your meeting area) to be selectable as the precise location for your meeting using the [Google Calendar integration for Work Adventure](/integrations/google-calendar). To do so, you must set the `meetingRoomLabel` property. You can provide any name that you would like your meeting room to have (as a string). @@ -86,3 +86,15 @@ and not {.alert.alert-info} When you use `jitsiUrl`, the targeted Jitsi instance must be public. You cannot use moderation features or the JWT tokens authentication with maps configured using the `jitsiUrl` property. + +## Full control over the Jitsi room name + +By default, the name of the room will be "slugified" and prepended with a hash of the room URL. +This is what you want most of the time. Indeed, different maps with the same Jitsi room name (the same `jitsiRoom` property) will not share the same Jitsi room instance. + +However, sometimes, you may actually want to have different WorkAdventure meeting rooms that are actually sharing +the same Jitsi meet meeting room. Or if you are pointing to a custom Jitsi server (using the `jitsiUrl` property), +you may want to point to a specific existing room. + +For all those use cases, you can use `jitsiNoPrefix: true`. This will remove the automatic prefixing +of the hash and will give you full control on the Jitsi room name. diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index a76e3f31..4e896572 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -15,14 +15,9 @@ export interface RoomRedirect { export class Room { public readonly id: string; - /** - * @deprecated - */ - private readonly isPublic: boolean; private _authenticationMandatory: boolean = DISABLE_ANONYMOUS; private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER; private _mapUrl: string | undefined; - private instance: string | undefined; private readonly _search: URLSearchParams; private _contactPage: string | undefined; private _group: string | null = null; @@ -37,13 +32,6 @@ export class Room { if (this.id.startsWith("/")) { this.id = this.id.substr(1); } - if (this.id.startsWith("_/") || this.id.startsWith("*/")) { - this.isPublic = true; - } else if (this.id.startsWith("@/")) { - this.isPublic = false; - } else { - throw new Error("Invalid room ID"); - } this._search = new URLSearchParams(roomUrl.search); } @@ -84,8 +72,10 @@ export class Room { const currentRoom = new Room(baseUrl); let instance: string = "global"; - if (currentRoom.isPublic) { - instance = currentRoom.getInstance(); + if (currentRoom.id.startsWith("_/") || currentRoom.id.startsWith("*/")) { + const match = /[_*]\/([^/]+)\/.+/.exec(currentRoom.id); + if (!match) throw new Error('Could not extract instance from "' + currentRoom.id + '"'); + instance = match[1]; } baseUrl.pathname = "/_/" + instance + "/" + absoluteExitSceneUrl.host + absoluteExitSceneUrl.pathname; @@ -151,31 +141,6 @@ export class Room { } } - /** - * Instance name is: - * - In a public URL: the second part of the URL ( _/[instance]/map.json) - * - In a private URL: [organizationId/worldId] - * - * @deprecated - */ - public getInstance(): string { - if (this.instance !== undefined) { - return this.instance; - } - - if (this.isPublic) { - const match = /[_*]\/([^/]+)\/.+/.exec(this.id); - if (!match) throw new Error('Could not extract instance from "' + this.id + '"'); - this.instance = match[1]; - return this.instance; - } else { - const match = /@\/([^/]+)\/([^/]+)\/.+/.exec(this.id); - if (!match) throw new Error('Could not extract instance from "' + this.id + '"'); - this.instance = match[1] + "/" + match[2]; - return this.instance; - } - } - public isDisconnected(): boolean { const alone = this._search.get("alone"); if (alone && alone !== "0" && alone.toLowerCase() !== "false") { diff --git a/front/src/Phaser/Game/GameMapProperties.ts b/front/src/Phaser/Game/GameMapProperties.ts index 1c2a78b6..f905158b 100644 --- a/front/src/Phaser/Game/GameMapProperties.ts +++ b/front/src/Phaser/Game/GameMapProperties.ts @@ -16,6 +16,7 @@ export enum GameMapProperties { JITSI_TRIGGER_MESSAGE = "jitsiTriggerMessage", JITSI_URL = "jitsiUrl", JITSI_WIDTH = "jitsiWidth", + JITSI_NO_PREFIX = "jitsiNoPrefix", NAME = "name", OPEN_TAB = "openTab", OPEN_WEBSITE = "openWebsite", diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 1e25c576..e94c02df 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -79,7 +79,11 @@ export class GameMapPropertiesListener { }); } else { const openJitsiRoomFunction = () => { - const roomName = jitsiFactory.getRoomName(newValue.toString(), this.scene.instance); + let addPrefix = true; + if (allProps.get(GameMapProperties.JITSI_NO_PREFIX)) { + addPrefix = false; + } + const roomName = jitsiFactory.getRoomName(newValue.toString(), this.scene.roomUrl, addPrefix); const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; if (JITSI_PRIVATE_MODE && !jitsiUrl) { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 5914d31e..85e6e614 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -185,7 +185,6 @@ export class GameScene extends DirtyScene { private biggestAvailableAreaStoreUnsubscribe!: () => void; MapUrlFile: string; roomUrl: string; - instance: string; currentTick!: number; lastSentTick!: number; // The last tick at which a position was sent. @@ -234,7 +233,6 @@ export class GameScene extends DirtyScene { }); this.Terrains = []; this.groups = new Map(); - this.instance = room.getInstance(); this.MapUrlFile = MapUrlFile; this.roomUrl = room.key; diff --git a/front/src/Utils/StringUtils.ts b/front/src/Utils/StringUtils.ts index 1f168c15..8c5f16db 100644 --- a/front/src/Utils/StringUtils.ts +++ b/front/src/Utils/StringUtils.ts @@ -9,4 +9,21 @@ export class StringUtils { } return { x: values[0], y: values[1] }; } + + /** + * Computes a "short URL" hash of the string passed in parameter. + */ + public static shortHash = function (s: string): string { + let hash = 0; + const strLength = s.length; + if (strLength === 0) { + return ""; + } + for (let i = 0; i < strLength; i++) { + const c = s.charCodeAt(i); + hash = (hash << 5) - hash + c; + hash = hash & hash; // Convert to 32bit integer + } + return Math.abs(hash).toString(36); + }; } diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 3d17b5b8..62800888 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -5,6 +5,7 @@ import { get } from "svelte/store"; import CancelablePromise from "cancelable-promise"; import { gameManager } from "../Phaser/Game/GameManager"; import { jitsiParticipantsCountStore, userIsJitsiDominantSpeakerStore } from "../Stores/GameStore"; +import { StringUtils } from "../Utils/StringUtils"; interface jitsiConfigInterface { startWithAudioMuted: boolean; @@ -120,7 +121,7 @@ const slugify = (...args: (string | number)[]): string => { .replace(/[\u0300-\u036f]/g, "") // remove all previously split accents .toLowerCase() .trim() - .replace(/[^a-z0-9 ]/g, "") // remove all chars not letters, numbers and spaces (to be replaced) + .replace(/[^a-z0-9-_ ]/g, "") // remove all chars not letters, numbers, dash, underscores and spaces (to be replaced) .replace(/\s+/g, "-"); // separator }; @@ -135,8 +136,8 @@ class JitsiFactory { /** * Slugifies the room name and prepends the room name with the instance */ - public getRoomName(roomName: string, instance: string): string { - return slugify(instance.replace("/", "-") + "-" + roomName); + public getRoomName(roomName: string, roomId: string, addPrefix: boolean): string { + return slugify((addPrefix ? StringUtils.shortHash(roomId) + "-" : "") + roomName); } public start( diff --git a/front/src/i18n/de-DE/report.ts b/front/src/i18n/de-DE/report.ts index 438642eb..7ddb530f 100644 --- a/front/src/i18n/de-DE/report.ts +++ b/front/src/i18n/de-DE/report.ts @@ -8,7 +8,8 @@ const report: NonNullable = { block: "Blockiere diesen Nutzer", }, title: "Melden", - content: "Verfasse eine Beschwerde an die Administratoren dieses Raums. Diese können den Nutzer anschließend bannen.", + content: + "Verfasse eine Beschwerde an die Administratoren dieses Raums. Diese können den Nutzer anschließend bannen.", message: { title: "Deine Nachricht: ", empty: "Bitte Text eingeben.", diff --git a/maps/tests/jitsi_config.json b/maps/tests/jitsi_config.json index 9a812cc8..d0a446e1 100644 --- a/maps/tests/jitsi_config.json +++ b/maps/tests/jitsi_config.json @@ -43,6 +43,11 @@ "type":"string", "value":"{\"DEFAULT_BACKGROUND\":\"#77ee77\"}" }, + { + "name":"jitsiNoPrefix", + "type":"bool", + "value":true + }, { "name":"jitsiRoom", "type":"string", @@ -65,7 +70,7 @@ "name":"floorLayer", "objects":[ { - "height":83.6666666666666, + "height":110.891622876526, "id":1, "name":"", "rotation":0, @@ -73,14 +78,14 @@ { "fontfamily":"Sans Serif", "pixelsize":13, - "text":"Test:\nWalk on the carpet and press space\nResult:\nJitsi opens, background in green and audio\/video is muted", + "text":"Test:\nWalk on the carpet and press space\nResult:\nJitsi opens, background in green and audio\/video is muted.\nThe name of the room (displayed at the top of Jitsi) is \"Myroom Avec Espace EA\"", "wrap":true }, "type":"", "visible":true, "width":315.4375, - "x":2.28125, - "y":235.166666666667 + "x":1.48051599382768, + "y":209.535838407429 }], "opacity":1, "type":"objectgroup",