Merge pull request #4382 from matrix-org/travis/addwidget-improvements

Allow iframes and Jitsi URLs in /addwidget
This commit is contained in:
Travis Ralston 2020-04-21 00:30:46 -06:00 committed by GitHub
commit 5dca84379f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 5 deletions

View file

@ -82,6 +82,7 @@
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"pako": "^1.0.5", "pako": "^1.0.5",
"parse5": "^5.1.1",
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"project-name-generator": "^2.1.7", "project-name-generator": "^2.1.7",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",

View file

@ -36,6 +36,8 @@ import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/I
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks"; import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
import {inviteUsersToRoom} from "./RoomInvite"; import {inviteUsersToRoom} from "./RoomInvite";
import { WidgetType } from "./widgets/WidgetType"; import { WidgetType } from "./widgets/WidgetType";
import { Jitsi } from "./widgets/Jitsi";
import { parseFragment as parseHtml } from "parse5";
import sendBugReport from "./rageshake/submit-rageshake"; import sendBugReport from "./rageshake/submit-rageshake";
import SdkConfig from "./SdkConfig"; import SdkConfig from "./SdkConfig";
@ -769,18 +771,50 @@ export const Commands = [
}), }),
new Command({ new Command({
command: 'addwidget', command: 'addwidget',
args: '<url>', args: '<url | embed code | Jitsi url>',
description: _td('Adds a custom widget by URL to the room'), description: _td('Adds a custom widget by URL to the room'),
runFn: function(roomId, args) { runFn: function(roomId, widgetUrl) {
if (!args || (!args.startsWith("https://") && !args.startsWith("http://"))) { if (!widgetUrl) {
return reject(_t("Please supply a widget URL or embed code"));
}
// Try and parse out a widget URL from iframes
if (widgetUrl.toLowerCase().startsWith("<iframe ")) {
// We use parse5, which doesn't render/create a DOM node. It instead runs
// some superfast regex over the text so we don't have to.
const embed = parseHtml(widgetUrl);
if (embed && embed.childNodes && embed.childNodes.length === 1) {
const iframe = embed.childNodes[0];
if (iframe.tagName.toLowerCase() === 'iframe' && iframe.attrs) {
const srcAttr = iframe.attrs.find(a => a.name === 'src');
console.log("Pulling URL out of iframe (embed code)");
widgetUrl = srcAttr.value;
}
}
}
if (!widgetUrl.startsWith("https://") && !widgetUrl.startsWith("http://")) {
return reject(_t("Please supply a https:// or http:// widget URL")); return reject(_t("Please supply a https:// or http:// widget URL"));
} }
if (WidgetUtils.canUserModifyWidgets(roomId)) { if (WidgetUtils.canUserModifyWidgets(roomId)) {
const userId = MatrixClientPeg.get().getUserId(); const userId = MatrixClientPeg.get().getUserId();
const nowMs = (new Date()).getTime(); const nowMs = (new Date()).getTime();
const widgetId = encodeURIComponent(`${roomId}_${userId}_${nowMs}`); const widgetId = encodeURIComponent(`${roomId}_${userId}_${nowMs}`);
return success(WidgetUtils.setRoomWidget( let type = WidgetType.CUSTOM;
roomId, widgetId, WidgetType.CUSTOM, args, "Custom Widget", {})); let name = "Custom Widget";
let data = {};
// Make the widget a Jitsi widget if it looks like a Jitsi widget
const jitsiData = Jitsi.getInstance().parsePreferredConferenceUrl(widgetUrl);
if (jitsiData) {
console.log("Making /addwidget widget a Jitsi conference");
type = WidgetType.JITSI;
name = "Jitsi Conference";
data = jitsiData;
widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl();
}
return success(WidgetUtils.setRoomWidget(roomId, widgetId, type, widgetUrl, name, data));
} else { } else {
return reject(_t("You cannot modify widgets in this room.")); return reject(_t("You cannot modify widgets in this room."));
} }

View file

@ -202,6 +202,7 @@
"Deops user with given id": "Deops user with given id", "Deops user with given id": "Deops user with given id",
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog", "Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
"Adds a custom widget by URL to the room": "Adds a custom widget by URL to the room", "Adds a custom widget by URL to the room": "Adds a custom widget by URL to the room",
"Please supply a widget URL or embed code": "Please supply a widget URL or embed code",
"Please supply a https:// or http:// widget URL": "Please supply a https:// or http:// widget URL", "Please supply a https:// or http:// widget URL": "Please supply a https:// or http:// widget URL",
"You cannot modify widgets in this room.": "You cannot modify widgets in this room.", "You cannot modify widgets in this room.": "You cannot modify widgets in this room.",
"Verifies a user, session, and pubkey tuple": "Verifies a user, session, and pubkey tuple", "Verifies a user, session, and pubkey tuple": "Verifies a user, session, and pubkey tuple",

View file

@ -21,6 +21,12 @@ import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
const JITSI_WK_PROPERTY = "im.vector.riot.jitsi"; const JITSI_WK_PROPERTY = "im.vector.riot.jitsi";
const JITSI_WK_CHECK_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours, arbitrarily selected const JITSI_WK_CHECK_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours, arbitrarily selected
export interface JitsiWidgetData {
conferenceId: string;
isAudioOnly: boolean;
domain: string;
}
export class Jitsi { export class Jitsi {
private static instance: Jitsi; private static instance: Jitsi;
@ -64,6 +70,22 @@ export class Jitsi {
console.log("Jitsi conference domain:", this.preferredDomain); console.log("Jitsi conference domain:", this.preferredDomain);
} }
/**
* Parses the given URL into the data needed for a Jitsi widget, if the widget
* URL matches the preferredDomain for the app.
* @param {string} url The URL to parse.
* @returns {JitsiWidgetData} The widget data if eligible, otherwise null.
*/
public parsePreferredConferenceUrl(url: string): JitsiWidgetData {
const parsed = new URL(url);
if (parsed.hostname !== this.preferredDomain) return null; // invalid
return {
conferenceId: parsed.pathname,
domain: parsed.hostname,
isAudioOnly: false,
};
}
public static getInstance(): Jitsi { public static getInstance(): Jitsi {
if (!Jitsi.instance) { if (!Jitsi.instance) {
Jitsi.instance = new Jitsi(); Jitsi.instance = new Jitsi();

View file

@ -6434,6 +6434,11 @@ parse5@^3.0.1:
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
parse5@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
pascalcase@^0.1.1: pascalcase@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"