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",
|
"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",
|
||||||
|
|
|
@ -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."));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue