diff --git a/config/integrations/etherpad_widget.yaml b/config/integrations/etherpad_widget.yaml new file mode 100644 index 0000000..53aeb5a --- /dev/null +++ b/config/integrations/etherpad_widget.yaml @@ -0,0 +1,7 @@ +# All this configuration does is make "Etherpad Widget" available in the UI +type: "widget" +integrationType: "etherpad" +enabled: true +name: "Etherpad" +about: "Etherpad is a collaborative text editor. With this widget you can embed Etherpad directly in to your Matrix chat rooms so that you can securely collaborate on documents." +avatar: "img/avatars/etherpad.png" diff --git a/web/app/app.module.ts b/web/app/app.module.ts index b62fe1e..04eeb30 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -29,6 +29,7 @@ import { ToggleFullscreenDirective } from "./shared/toggle-fullscreen.directive" import { FullscreenButtonComponent } from "./fullscreen-button/fullscreen-button.component"; import { YoutubeWidgetConfigComponent } from "./configs/widget/youtube/youtube-config.component"; import { TwitchWidgetConfigComponent } from "./configs/widget/twitch/twitch-config.component"; +import { EtherpadWidgetConfigComponent } from "./configs/widget/etherpad/etherpad-config.component"; import { VideoWidgetWrapperComponent } from "./widget_wrappers/video/video.component"; @NgModule({ @@ -60,6 +61,7 @@ import { VideoWidgetWrapperComponent } from "./widget_wrappers/video/video.compo FullscreenButtonComponent, YoutubeWidgetConfigComponent, TwitchWidgetConfigComponent, + EtherpadWidgetConfigComponent, VideoWidgetWrapperComponent, // Vendor @@ -80,7 +82,8 @@ import { VideoWidgetWrapperComponent } from "./widget_wrappers/video/video.compo IrcConfigComponent, CustomWidgetConfigComponent, YoutubeWidgetConfigComponent, - TwitchWidgetConfigComponent + TwitchWidgetConfigComponent, + EtherpadWidgetConfigComponent, ] }) export class AppModule { diff --git a/web/app/configs/widget/etherpad/etherpad-config.component.html b/web/app/configs/widget/etherpad/etherpad-config.component.html new file mode 100644 index 0000000..09d4f19 --- /dev/null +++ b/web/app/configs/widget/etherpad/etherpad-config.component.html @@ -0,0 +1,118 @@ +
+ +
+ +

Configure Etherpad widgets

+
+
+
+
+

Loading widgets...

+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+
+ {{ widget.name || widget.url }} (added by {{ widget.ownerId }}) + + +
+
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+
+
+
+
+
diff --git a/web/app/configs/widget/etherpad/etherpad-config.component.scss b/web/app/configs/widget/etherpad/etherpad-config.component.scss new file mode 100644 index 0000000..92dce18 --- /dev/null +++ b/web/app/configs/widget/etherpad/etherpad-config.component.scss @@ -0,0 +1,4 @@ +// component styles are encapsulated and only applied to their components +.widget-item { + margin-top: 3px; +} diff --git a/web/app/configs/widget/etherpad/etherpad-config.component.ts b/web/app/configs/widget/etherpad/etherpad-config.component.ts new file mode 100644 index 0000000..0400fb7 --- /dev/null +++ b/web/app/configs/widget/etherpad/etherpad-config.component.ts @@ -0,0 +1,124 @@ +import { Component } from "@angular/core"; +import { ModalComponent, DialogRef } from "ngx-modialog"; +import { WidgetComponent } from "../widget.component"; +import { ScalarService } from "../../../shared/scalar.service"; +import { ConfigModalContext } from "../../../integration/integration.component"; +import { ToasterService } from "angular2-toaster"; +import { Widget, WIDGET_DIM_ETHERPAD, WIDGET_SCALAR_ETHERPAD } from "../../../shared/models/widget"; + +@Component({ + selector: "my-etherpadwidget-config", + templateUrl: "etherpad-config.component.html", + styleUrls: ["etherpad-config.component.scss", "./../../config.component.scss"], +}) +export class EtherpadWidgetConfigComponent extends WidgetComponent implements ModalComponent { + + public newEtherpadServerUrl: string = ""; + public useCustomServer = false; + public editUseCustomServer = false; + + constructor(public dialog: DialogRef, + toaster: ToasterService, + scalarService: ScalarService, + window: Window) { + super( + toaster, + scalarService, + dialog.context.roomId, + window, + WIDGET_DIM_ETHERPAD, + WIDGET_SCALAR_ETHERPAD, + dialog.context.integrationId, + "Etherpad Widget", + "generic", // wrapper + "etherpad" // scalar wrapper + ); + } + + getPadURL(widget?: Widget): string { + let url: string; + if (widget) { + if (this.editUseCustomServer) { + url = widget.data.newPadServer + this.roomId + "_" + widget.data.newPadName; + } else { + url = "https://demo.riot.im/etherpad/p/" + this.roomId + "_" + widget.data.newPadName; + } + } else { + if (this.useCustomServer) { + url = this.newEtherpadServerUrl + this.roomId + "_" + this.newWidgetUrl; + } else { + url = "https://demo.riot.im/etherpad/p/" + this.roomId + "_" + this.newWidgetUrl; + } + } + return url; + } + + private checkPadURL(url: string, widget?: Widget): boolean { + if (widget) { + if (this.editUseCustomServer) { + return Boolean(url === widget.data.newPadServer); + } else { + return Boolean(url === "https://demo.riot.im/etherpad/p/"); + } + } else { + if (this.useCustomServer) { + return Boolean(url === this.newEtherpadServerUrl); + } else { + return Boolean(url === "https://demo.riot.im/etherpad/p/"); + } + } + } + + public validateAndAddWidget() { + const url = this.getPadURL() + "?userName=$matrix_user_id"; + + if (this.checkPadURL(url)) { + this.toaster.pop("warning", "Please enter a Pad Name"); + return; + } + + const originalUrl = this.roomId + "_" + this.newWidgetUrl; + this.newWidgetUrl = url; + + if (this.useCustomServer) { + this.addWidget({padName: originalUrl, padSuffix: originalUrl, padServer: this.newEtherpadServerUrl}); + } else { + this.addWidget({padName: originalUrl, padSuffix: originalUrl, padServer: "https://demo.riot.im/etherpad/p/"}); + } + } + + public validateAndSaveWidget(widget: Widget) { + const url = this.getPadURL(widget) + "?userName=$matrix_user_id"; + + if (this.checkPadURL(url, widget)) { + this.toaster.pop("warning", "Please enter a Pad Name"); + return; + } + + if (!widget.data) widget.data = {}; + + widget.newUrl = url; + widget.data.padName = widget.data.newPadName; + widget.data.padSuffix = this.roomId + "_" + widget.data.newPadName; + if (this.editUseCustomServer) { + widget.data.padServer = widget.data.newPadServer; + } else { + widget.data.padServer = "https://demo.riot.im/etherpad/p/" + } + + delete widget.data.newPadServer; + delete widget.data.newPadName; + + this.saveWidget(widget); + } + + editWidget(widget: Widget) { + widget.data.newPadName = widget.data.padName; + widget.data.newPadServer = widget.data.padServer; + if (widget.data.newPadServer !== "https://demo.riot.im/etherpad/p/") { + this.editUseCustomServer = true + } + super.editWidget(widget); + } + +} diff --git a/web/app/configs/widget/widget.component.ts b/web/app/configs/widget/widget.component.ts index 4692e45..f3a966e 100644 --- a/web/app/configs/widget/widget.component.ts +++ b/web/app/configs/widget/widget.component.ts @@ -88,7 +88,16 @@ export class WidgetComponent { } private wrapUrl(url: string): string { - return this.wrapperUrl + encodeURIComponent(url); + let encodedURL = this.wrapperUrl + encodeURIComponent(url); + + //don't URL encode $vars of the widget Spec + //TODO do the same with vars from the data object + encodedURL = encodedURL.replace(encodeURIComponent("$matrix_user_id"), "$matrix_user_id"); + encodedURL = encodedURL.replace(encodeURIComponent("$matrix_room_id"), "$matrix_room_id"); + encodedURL = encodedURL.replace(encodeURIComponent("$matrix_display_name"), "$matrix_display_name"); + encodedURL = encodedURL.replace(encodeURIComponent("$matrix_avatar_url"), "$matrix_avatar_url"); + + return encodedURL; } private setWidgetUrl(widget: Widget) { diff --git a/web/app/riot/riot.component.ts b/web/app/riot/riot.component.ts index 83fd31f..2094a7c 100644 --- a/web/app/riot/riot.component.ts +++ b/web/app/riot/riot.component.ts @@ -6,7 +6,7 @@ import { ToasterService } from "angular2-toaster"; import { Integration } from "../shared/models/integration"; import { IntegrationService } from "../shared/integration.service"; import * as _ from "lodash"; -import { WIDGET_DIM_CUSTOM, WIDGET_DIM_YOUTUBE, WIDGET_DIM_TWITCH } from "../shared/models/widget"; +import { WIDGET_DIM_CUSTOM, WIDGET_DIM_YOUTUBE, WIDGET_DIM_TWITCH, WIDGET_DIM_ETHERPAD } from "../shared/models/widget"; import { IntegrationComponent } from "../integration/integration.component"; @Component({ @@ -79,6 +79,9 @@ export class RiotComponent { } else if (this.requestedScreen === "type_" + WIDGET_DIM_TWITCH) { type = "widget"; integrationType = "twitch"; + } else if (this.requestedScreen === "type_" + WIDGET_DIM_ETHERPAD) { + type = "widget"; + integrationType = "etherpad"; } else { console.log("Unknown screen requested: " + this.requestedScreen); } diff --git a/web/app/shared/integration.service.ts b/web/app/shared/integration.service.ts index 46f2229..2a429e0 100644 --- a/web/app/shared/integration.service.ts +++ b/web/app/shared/integration.service.ts @@ -7,6 +7,7 @@ import { TravisCiConfigComponent } from "../configs/travisci/travisci-config.com import { CustomWidgetConfigComponent } from "../configs/widget/custom_widget/custom_widget-config.component"; import { YoutubeWidgetConfigComponent } from "../configs/widget/youtube/youtube-config.component"; import { TwitchWidgetConfigComponent } from "../configs/widget/twitch/twitch-config.component"; +import { EtherpadWidgetConfigComponent } from "../configs/widget/etherpad/etherpad-config.component"; @Injectable() export class IntegrationService { @@ -24,6 +25,7 @@ export class IntegrationService { "customwidget": true, "youtube": true, "twitch": true, + "etherpad": true, }, }; @@ -39,6 +41,7 @@ export class IntegrationService { "customwidget": CustomWidgetConfigComponent, "youtube": YoutubeWidgetConfigComponent, "twitch": TwitchWidgetConfigComponent, + "etherpad": EtherpadWidgetConfigComponent, }, }; diff --git a/web/app/shared/models/widget.ts b/web/app/shared/models/widget.ts index 2186add..e921e0e 100644 --- a/web/app/shared/models/widget.ts +++ b/web/app/shared/models/widget.ts @@ -14,6 +14,7 @@ export const WIDGET_SCALAR_TWITCH = ""; export const WIDGET_DIM_CUSTOM = "dimension-customwidget"; export const WIDGET_DIM_YOUTUBE = "dimension-youtube"; export const WIDGET_DIM_TWITCH = "dimension-twitch"; +export const WIDGET_DIM_ETHERPAD = "dimension-etherpad"; export interface Widget { id: string; diff --git a/web/public/img/avatars/etherpad.png b/web/public/img/avatars/etherpad.png new file mode 100644 index 0000000..c420f30 Binary files /dev/null and b/web/public/img/avatars/etherpad.png differ