Merge pull request #4382 from matrix-org/travis/addwidget-improvements
Allow iframes and Jitsi URLs in /addwidget
This commit is contained in:
commit
5dca84379f
5 changed files with 68 additions and 5 deletions
|
@ -82,6 +82,7 @@
|
|||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"minimist": "^1.2.0",
|
||||
"pako": "^1.0.5",
|
||||
"parse5": "^5.1.1",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"project-name-generator": "^2.1.7",
|
||||
"prop-types": "^15.5.8",
|
||||
|
|
|
@ -36,6 +36,8 @@ import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/I
|
|||
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
|
||||
import {inviteUsersToRoom} from "./RoomInvite";
|
||||
import { WidgetType } from "./widgets/WidgetType";
|
||||
import { Jitsi } from "./widgets/Jitsi";
|
||||
import { parseFragment as parseHtml } from "parse5";
|
||||
import sendBugReport from "./rageshake/submit-rageshake";
|
||||
import SdkConfig from "./SdkConfig";
|
||||
|
||||
|
@ -769,18 +771,50 @@ export const Commands = [
|
|||
}),
|
||||
new Command({
|
||||
command: 'addwidget',
|
||||
args: '<url>',
|
||||
args: '<url | embed code | Jitsi url>',
|
||||
description: _td('Adds a custom widget by URL to the room'),
|
||||
runFn: function(roomId, args) {
|
||||
if (!args || (!args.startsWith("https://") && !args.startsWith("http://"))) {
|
||||
runFn: function(roomId, widgetUrl) {
|
||||
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"));
|
||||
}
|
||||
if (WidgetUtils.canUserModifyWidgets(roomId)) {
|
||||
const userId = MatrixClientPeg.get().getUserId();
|
||||
const nowMs = (new Date()).getTime();
|
||||
const widgetId = encodeURIComponent(`${roomId}_${userId}_${nowMs}`);
|
||||
return success(WidgetUtils.setRoomWidget(
|
||||
roomId, widgetId, WidgetType.CUSTOM, args, "Custom Widget", {}));
|
||||
let type = WidgetType.CUSTOM;
|
||||
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 {
|
||||
return reject(_t("You cannot modify widgets in this room."));
|
||||
}
|
||||
|
|
|
@ -202,6 +202,7 @@
|
|||
"Deops user with given id": "Deops user with given id",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
|
|
|
@ -21,6 +21,12 @@ import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
|
|||
const JITSI_WK_PROPERTY = "im.vector.riot.jitsi";
|
||||
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 {
|
||||
private static instance: Jitsi;
|
||||
|
||||
|
@ -64,6 +70,22 @@ export class Jitsi {
|
|||
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 {
|
||||
if (!Jitsi.instance) {
|
||||
Jitsi.instance = new Jitsi();
|
||||
|
|
|
@ -6434,6 +6434,11 @@ parse5@^3.0.1:
|
|||
dependencies:
|
||||
"@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:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||
|
|
Loading…
Reference in a new issue