First commit
This commit is contained in:
commit
e66d8a13e8
10 changed files with 269 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.DS_Store
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# discourse-bbb
|
||||||
|
|
||||||
|
Discourse integration with BigBlueButton.
|
51
app/controllers/bbb_client_controller.rb
Normal file
51
app/controllers/bbb_client_controller.rb
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'digest/sha1'
|
||||||
|
|
||||||
|
module BigBlue
|
||||||
|
class BbbClientController < ApplicationController
|
||||||
|
before_action :ensure_logged_in
|
||||||
|
|
||||||
|
def create
|
||||||
|
render json: {
|
||||||
|
url: build_url(params)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def build_url(args)
|
||||||
|
return false unless SiteSetting.bbb_endpoint && SiteSetting.bbb_secret
|
||||||
|
|
||||||
|
meeting_id = args['meetingID']
|
||||||
|
url = SiteSetting.bbb_endpoint
|
||||||
|
secret = SiteSetting.bbb_secret
|
||||||
|
attendee_pw = args['attendeePW']
|
||||||
|
moderator_pw = args['moderatorPW']
|
||||||
|
|
||||||
|
query = {
|
||||||
|
meetingID: meeting_id,
|
||||||
|
attendeePW: attendee_pw,
|
||||||
|
moderatorPW: moderator_pw
|
||||||
|
}.to_query
|
||||||
|
|
||||||
|
checksum = Digest::SHA1.hexdigest ("create" + query + secret)
|
||||||
|
|
||||||
|
create_url = "#{url}create?#{query}&checksum=#{checksum}"
|
||||||
|
response = Excon.get(create_url)
|
||||||
|
|
||||||
|
if response.status != 200
|
||||||
|
Rails.logger.warn("Could not create meeting: #{response.inspect}")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
join_params = {
|
||||||
|
fullName: current_user.name || current_user.username,
|
||||||
|
meetingID: meeting_id,
|
||||||
|
password: attendee_pw # TODO: pass moderator username or staff as moderator?
|
||||||
|
}.to_query
|
||||||
|
|
||||||
|
join_checksum = Digest::SHA1.hexdigest ("join" + join_params + secret)
|
||||||
|
"#{url}join?#{join_params}&checksum=#{join_checksum}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,34 @@
|
||||||
|
import Controller from "@ember/controller";
|
||||||
|
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||||
|
|
||||||
|
export default Controller.extend(ModalFunctionality, {
|
||||||
|
keyDown(e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
this.setProperties({
|
||||||
|
meetingID: "",
|
||||||
|
attendeePW: "",
|
||||||
|
moderatorPW: "",
|
||||||
|
buttonText: ""
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
insert() {
|
||||||
|
const btnTxt = this.buttonText ? ` label="${this.buttonText}"` : "";
|
||||||
|
this.toolbarEvent.addText(
|
||||||
|
`[wrap=discourse-bbb meetingID="${this.meetingID}"${btnTxt} attendeePW="${this.attendeePW}" moderatorPW="${this.moderatorPW}"][/wrap]`
|
||||||
|
);
|
||||||
|
this.send("closeModal");
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
this.send("closeModal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
80
assets/javascripts/discourse/initializers/bbb.js.es6
Normal file
80
assets/javascripts/discourse/initializers/bbb.js.es6
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
|
import showModal from "discourse/lib/show-modal";
|
||||||
|
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
|
function launchBBB($elem, fullWindow) {
|
||||||
|
const data = $elem.data();
|
||||||
|
|
||||||
|
ajax("/bbb/create.json", {
|
||||||
|
type: "POST",
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.url) {
|
||||||
|
console.log(fullWindow);
|
||||||
|
if (fullWindow) {
|
||||||
|
window.location.href = res.url;
|
||||||
|
} else {
|
||||||
|
$elem.children().hide();
|
||||||
|
$elem.append(
|
||||||
|
`<iframe src="${res.url}" allow="camera;microphone;fullscreen;speaker" width="690" height="500" style="border:none"></iframe>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachButton($elem, fullWindow) {
|
||||||
|
const buttonLabel = $elem.data("label") || I18n.t("bbb.launch");
|
||||||
|
|
||||||
|
$elem.html(
|
||||||
|
`<button class='launch-bbb btn'>${iconHTML(
|
||||||
|
"video"
|
||||||
|
)} ${buttonLabel}</button>`
|
||||||
|
);
|
||||||
|
$elem.find("button").on("click", () => launchBBB($elem, fullWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachBBB($elem, helper) {
|
||||||
|
if (helper) {
|
||||||
|
const siteSettings = Discourse.__container__.lookup("site-settings:main");
|
||||||
|
const fullWindow = siteSettings.bbb_full_window;
|
||||||
|
|
||||||
|
$elem.find("[data-wrap=discourse-bbb]").each((idx, val) => {
|
||||||
|
attachButton($(val), fullWindow);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "insert-bbb",
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
withPluginApi("0.8.31", api => {
|
||||||
|
const currentUser = api.getCurrentUser();
|
||||||
|
const siteSettings = api.container.lookup("site-settings:main");
|
||||||
|
|
||||||
|
api.onToolbarCreate(toolbar => {
|
||||||
|
if (siteSettings.bbb_staff_only && !currentUser.staff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar.addButton({
|
||||||
|
title: "bbb.composer_title",
|
||||||
|
id: "insertBBB",
|
||||||
|
group: "insertions",
|
||||||
|
icon: "fab-bootstrap",
|
||||||
|
perform: e =>
|
||||||
|
showModal("insert-bbb").setProperties({ toolbarEvent: e })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
api.decorateCooked(attachBBB, { id: "discourse-bbb" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
28
assets/javascripts/discourse/templates/modal/insert-bbb.hbs
Normal file
28
assets/javascripts/discourse/templates/modal/insert-bbb.hbs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{{#d-modal-body title="bbb.modal.title" class="insert-bbb"}}
|
||||||
|
<div class="insert-bbb-form">
|
||||||
|
<div class="insert-bbb-input">
|
||||||
|
<label>{{i18n "bbb.meetingID"}}</label>
|
||||||
|
{{text-field value=meetingID}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="insert-bbb-input">
|
||||||
|
<label>{{i18n "bbb.button_text"}}</label>
|
||||||
|
{{text-field value=buttonText placeholderKey="bbb.launch"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="insert-bbb-input">
|
||||||
|
<label>{{i18n "bbb.attendeePW"}}</label>
|
||||||
|
{{text-field value=attendeePW}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="insert-bbb-input">
|
||||||
|
<label>{{i18n "bbb.moderatorPW"}}</label>
|
||||||
|
{{text-field value=moderatorPW}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/d-modal-body}}
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
{{d-button class="btn-primary" disabled=insertDisabled label="bbb.modal.insert" action=(action "insert")}}
|
||||||
|
{{d-button class="btn-danger" label="bbb.modal.cancel" action=(action "cancel")}}
|
||||||
|
</div>
|
13
config/locales/client.en.yml
Normal file
13
config/locales/client.en.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
en:
|
||||||
|
js:
|
||||||
|
bbb:
|
||||||
|
composer_title: BigBlueButton Integration
|
||||||
|
meetingID: Meeting ID
|
||||||
|
button_text: Button label (optional)
|
||||||
|
attendeePW: Attendee password
|
||||||
|
moderatorPW: Moderator password
|
||||||
|
launch: Start Video Conference
|
||||||
|
modal:
|
||||||
|
insert: Insert
|
||||||
|
cancel: Cancel
|
||||||
|
title: Add BBB Integration
|
7
config/locales/server.en.yml
Normal file
7
config/locales/server.en.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
en:
|
||||||
|
site_settings:
|
||||||
|
bbb_enabled: "Enable BigBlueButton integration"
|
||||||
|
bbb_endpoint: "BigBlueButton Server URL (must include `api/`, as in: mysite.com/bigbluebutton/api/)."
|
||||||
|
bbb_secret: "BigBlueButton Shared Secret"
|
||||||
|
bbb_full_window: "If unchecked, video conference will load in an iframe."
|
||||||
|
bbb_staff_only: "Only show toolbar button to staff members."
|
17
config/settings.yml
Normal file
17
config/settings.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
plugins:
|
||||||
|
bbb_enabled:
|
||||||
|
default: false
|
||||||
|
client: true
|
||||||
|
bbb_endpoint:
|
||||||
|
default: ""
|
||||||
|
client: false
|
||||||
|
bbb_secret:
|
||||||
|
default: ""
|
||||||
|
client: false
|
||||||
|
secret: true
|
||||||
|
bbb_full_window:
|
||||||
|
default: true
|
||||||
|
client: true
|
||||||
|
bbb_staff_only:
|
||||||
|
default: true
|
||||||
|
client: true
|
35
plugin.rb
Normal file
35
plugin.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# name: discourse-bbb
|
||||||
|
# about: Integrate BigBlueButton in Discourse.
|
||||||
|
# version: 1.0.0
|
||||||
|
# authors: Penar Musaraj
|
||||||
|
# url: https://github.com/pmusaraj/discourse-bbb
|
||||||
|
|
||||||
|
enabled_site_setting :bbb_enabled
|
||||||
|
register_svg_icon "fab-bootstrap"
|
||||||
|
register_svg_icon "video"
|
||||||
|
|
||||||
|
after_initialize do
|
||||||
|
[
|
||||||
|
"../app/controllers/bbb_client_controller",
|
||||||
|
].each { |path| require File.expand_path(path, __FILE__) }
|
||||||
|
|
||||||
|
module ::BigBlue
|
||||||
|
PLUGIN_NAME ||= "discourse-bbb".freeze
|
||||||
|
|
||||||
|
class Engine < ::Rails::Engine
|
||||||
|
engine_name BigBlue::PLUGIN_NAME
|
||||||
|
isolate_namespace BigBlue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
BigBlue::Engine.routes.draw do
|
||||||
|
post '/create' => 'bbb_client#create', constraints: { format: :json }
|
||||||
|
end
|
||||||
|
|
||||||
|
Discourse::Application.routes.append do
|
||||||
|
mount ::BigBlue::Engine, at: "/bbb"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue