Merge pull request #98 from zach-chai/multi_meeting_rooms

Multi meeting rooms
This commit is contained in:
Zachary Chai 2017-01-31 11:15:25 -05:00 committed by GitHub
commit c70ad42d8b
18 changed files with 133 additions and 69 deletions

View File

@ -23,10 +23,11 @@
}); });
}; };
var initRooms = function() { var enableMeetingUpdates = function() {
App.messages = App.cable.subscriptions.create({ App.meeting_update = App.cable.subscriptions.create({
channel: 'MeetingUpdatesChannel', channel: 'MeetingUpdatesChannel',
encrypted_id: $(".page-wrapper").data('id') admin_id: $(".page-wrapper.rooms").data('admin-id'),
meeting_id: $(".page-wrapper.rooms").data('id')
}, },
{ {
received: function(data) { received: function(data) {
@ -55,10 +56,21 @@
}); });
}; };
var disableMeetingUpdates = function() {
App.meeting_update.unsubscribe();
delete App.meeting_update
};
$(document).on("turbolinks:load", function() { $(document).on("turbolinks:load", function() {
if ($("body[data-controller=landing]").get(0)) { if ($("body[data-controller=landing]").get(0)) {
if ($("body[data-action=rooms]").get(0)) { if ($("body[data-action=rooms]").get(0)) {
initRooms(); // disable meeting updates if enabled from a previous page
if (App.meeting_update) {
disableMeetingUpdates();
}
if ($(".page-wrapper.rooms").data('main-room') === false) {
enableMeetingUpdates();
}
} }
} }
}); });

View File

@ -64,6 +64,10 @@
} }
}); });
$('.center-panel-wrapper').on ('click', '.meeting-start', function (event) {
Turbolinks.visit(Meeting.getInstance().getURL());
});
$('.center-panel-wrapper').on ('keypress', '.meeting-user-name', function (event) { $('.center-panel-wrapper').on ('keypress', '.meeting-user-name', function (event) {
if (event.keyCode === 13) { if (event.keyCode === 13) {
event.preventDefault(); event.preventDefault();
@ -192,12 +196,15 @@
displayRoomURL(); displayRoomURL();
$('.center-panel-wrapper').on('keyup', '.meeting-name', function (event, msg) { $('.center-panel-wrapper').on('keyup', '.meeting-name', function (event, msg) {
var newName = $(this).val(); var newId = $(this).val();
// Meeting.getInstance().setMeetingId(newId); Meeting.getInstance().setMeetingId(newId);
Meeting.getInstance().setMeetingName(newName); $('.meeting-url').val(Meeting.getInstance().getURL());
// $(".page-wrapper.meetings").data('id', newId); $('.join-meeting-title').html(I18n.join_title.replace(/%{id}/, newId));
// $('.meeting-url').val(Meeting.getInstance().getURL()); if (newId === '') {
// $('.join-meeting-title').html(I18n.join_title.replace(/%{id}/, newId)); $('.invite-join-wrapper').addClass('hidden');
} else {
$('.invite-join-wrapper').removeClass('hidden');
}
}); });
Recordings.getInstance().refresh(); Recordings.getInstance().refresh();

View File

@ -19,28 +19,30 @@
_meetingInstance = null _meetingInstance = null
class @Meeting class @Meeting
constructor: (@meetingId, @type, @userName, @meetingName) -> constructor: (@meetingId, @type, @userName, @adminId) ->
# Gets the current instance or creates a new one # Gets the current instance or creates a new one
@getInstance: -> @getInstance: ->
if _meetingInstance if _meetingInstance
return _meetingInstance return _meetingInstance
id = $(".page-wrapper").data('id') meetingId = $(".page-wrapper").data('id')
if (type = location.pathname.split('/')[1]) != 'rooms' if (type = location.pathname.split('/')[1]) != 'rooms'
type = 'meetings' type = 'meetings'
name = $('.meeting-user-name').val() name = $('.meeting-user-name').val()
_meetingInstance = new Meeting(id, type, name) adminId = $(".page-wrapper").data('admin-id')
_meetingInstance = new Meeting(meetingId, type, name, adminId)
return _meetingInstance return _meetingInstance
@clear: -> @clear: ->
_meetingInstance = null _meetingInstance = null
@buildMeetingURL: (id, type, name) -> @buildMeetingURL: (meetingId, type, adminId) ->
if name fullId = ''
name = '/' + encodeURIComponent(name) if adminId
fullId = encodeURIComponent(adminId) + '/' + encodeURIComponent(meetingId)
else else
name = '' fullId = encodeURIComponent(meetingId)
return @buildFullDomainURL() + '/' + type + '/' + encodeURIComponent(id) + name return @buildFullDomainURL() + '/' + type + '/' + fullId
@buildFullDomainURL: -> @buildFullDomainURL: ->
url = location.protocol + '//' + location.hostname url = location.protocol + '//' + location.hostname
@ -84,8 +86,11 @@ class @Meeting
@meetingId = id @meetingId = id
return this return this
setMeetingName: (name) -> getAdminId: ->
@meetingName = name return @adminId
setAdminId: (id) ->
@adminId = id
return this return this
getType: -> getType: ->
@ -96,7 +101,7 @@ class @Meeting
return this return this
getURL: -> getURL: ->
return Meeting.buildMeetingURL(@meetingId, @type, @meetingName) return Meeting.buildMeetingURL(@meetingId, @type, @adminId)
getUserName: -> getUserName: ->
return @userName return @userName

View File

@ -157,7 +157,7 @@ class @Recordings
# refresh the recordings from the server # refresh the recordings from the server
refresh: -> refresh: ->
table_api = this.table.api() table_api = this.table.api()
$.get "/rooms/"+Meeting.getInstance().getMeetingId()+"/recordings", (data) => $.get "/rooms/"+Meeting.getInstance().getAdminId()+"/recordings", (data) =>
@setOwner(data.is_owner) @setOwner(data.is_owner)
if !@owner if !@owner
table_api.column(-1).visible(false) table_api.column(-1).visible(false)

View File

@ -16,6 +16,12 @@
class MeetingUpdatesChannel < ApplicationCable::Channel class MeetingUpdatesChannel < ApplicationCable::Channel
def subscribed def subscribed
stream_from "#{params[:encrypted_id]}_meeting_updates_channel"
full_id = if params[:meeting_id].present?
"#{params[:admin_id]}-#{params[:meeting_id]}"
else
params[:admin_id]
end
stream_from "#{full_id}_meeting_updates_channel"
end end
end end

View File

@ -42,11 +42,13 @@ class BbbController < ApplicationController
) )
end end
meeting_id = params[:room_id] meeting_id = "#{params[:room_id]}-#{params[:id]}"
meeting_name = params[:id] meeting_name = params[:id]
meeting_path = "#{params[:room_id]}/#{params[:id]}"
else else
meeting_id = params[:id]
user = User.find_by encrypted_id: params[:id] user = User.find_by encrypted_id: params[:id]
meeting_id = params[:id]
meeting_path = meeting_id
end end
options = if user options = if user
@ -62,7 +64,7 @@ class BbbController < ApplicationController
} }
end end
base_url = "#{request.base_url}/#{params[:resource]}/#{meeting_id}" base_url = "#{request.base_url}/#{params[:resource]}/#{meeting_path}"
options[:meeting_logout_url] = base_url options[:meeting_logout_url] = base_url
options[:hook_url] = "#{base_url}/callback" options[:hook_url] = "#{base_url}/callback"
@ -74,11 +76,11 @@ class BbbController < ApplicationController
# the user can join the meeting # the user can join the meeting
if bbb_res[:returncode] && current_user && current_user == user if bbb_res[:returncode] && current_user && current_user == user
JoinMeetingJob.perform_later(meeting_id) JoinMeetingJob.perform_later(user.encrypted_id, params[:id])
# user will be waiting for a moderator # user will be waiting for a moderator
else else
NotifyUserWaitingJob.perform_later(meeting_id, params[:name]) NotifyUserWaitingJob.perform_later(user.encrypted_id, params[:id], params[:name])
end end
render_bbb_response bbb_res, bbb_res[:response] render_bbb_response bbb_res, bbb_res[:response]
@ -103,9 +105,9 @@ class BbbController < ApplicationController
def end def end
load_and_authorize_room_owner! load_and_authorize_room_owner!
bbb_res = bbb_end_meeting @user.encrypted_id bbb_res = bbb_end_meeting "#{@user.encrypted_id}-#{params[:id]}"
if bbb_res[:returncode] if bbb_res[:returncode]
EndMeetingJob.perform_later(@user.encrypted_id) EndMeetingJob.perform_later(@user.encrypted_id, params[:id])
end end
render_bbb_response bbb_res render_bbb_response bbb_res
end end
@ -114,7 +116,8 @@ class BbbController < ApplicationController
def recordings def recordings
load_room! load_room!
bbb_res = bbb_get_recordings @user.encrypted_id # bbb_res = bbb_get_recordings "#{@user.encrypted_id}-#{params[:id]}"
bbb_res = bbb_get_recordings "#{@user.encrypted_id}"
render_bbb_response bbb_res, bbb_res[:recordings] render_bbb_response bbb_res, bbb_res[:recordings]
end end
@ -141,7 +144,7 @@ class BbbController < ApplicationController
private private
def load_room! def load_room!
@user = User.find_by encrypted_id: params[:id] @user = User.find_by encrypted_id: params[:room_id]
if !@user if !@user
render head(:not_found) && return render head(:not_found) && return
end end
@ -158,7 +161,7 @@ class BbbController < ApplicationController
def authorize_recording_owner! def authorize_recording_owner!
load_and_authorize_room_owner! load_and_authorize_room_owner!
recordings = bbb_get_recordings(params[:id])[:recordings] recordings = bbb_get_recordings(params[:room_id])[:recordings]
recordings.each do |recording| recordings.each do |recording|
if recording[:recordID] == params[:record_id] if recording[:recordID] == params[:record_id]
return true return true

View File

@ -35,12 +35,13 @@ class LandingController < ApplicationController
end end
def session_status_refresh def session_status_refresh
@user = User.find_by(encrypted_id: params[:id]) @user = User.find_by(encrypted_id: params[:room_id])
if @user.nil? if @user.nil?
render head(:not_found) && return render head(:not_found) && return
end end
@meeting_running = bbb_get_meeting_info(@user.encrypted_id)[:returncode] @meeting_id = params[:id]
@meeting_running = bbb_get_meeting_info("#{@user.encrypted_id}-#{params[:id]}")[:returncode]
render layout: false render layout: false
end end
@ -65,13 +66,15 @@ class LandingController < ApplicationController
def render_room def render_room
params[:action] = 'rooms' params[:action] = 'rooms'
@user = User.find_by(encrypted_id: params[:id]) @user = User.find_by(encrypted_id: params[:room_id] || params[:id])
if @user.nil? if @user.nil?
redirect_to root_path redirect_to root_path
return return
end end
@meeting_running = bbb_get_meeting_info(@user.encrypted_id)[:returncode] @meeting_id = params[:id]
@meeting_running = bbb_get_meeting_info("#{@user.encrypted_id}-#{@meeting_id}")[:returncode]
@main_room = @meeting_id.blank? || @meeting_id == @user.encrypted_id
render :action => 'rooms' render :action => 'rooms'
end end

View File

@ -17,6 +17,6 @@
module UsersHelper module UsersHelper
def is_room_owner def is_room_owner
token = current_user ? current_user.encrypted_id : nil token = current_user ? current_user.encrypted_id : nil
token.present? && params[:id].present? && token == params[:id] token.present? && params[:room_id].present? && token == params[:room_id]
end end
end end

View File

@ -19,15 +19,15 @@ class EndMeetingJob < ApplicationJob
queue_as :default queue_as :default
def perform(room) def perform(room, meeting)
tries = 0 tries = 0
sleep_time = 2 sleep_time = 2
while tries < 4 while tries < 4
bbb_res = bbb_get_meeting_info(room) bbb_res = bbb_get_meeting_info("#{room}-#{meeting}")
if !bbb_res[:returncode] if !bbb_res[:returncode]
ActionCable.server.broadcast "#{room}_meeting_updates_channel", ActionCable.server.broadcast "#{room}-#{meeting}_meeting_updates_channel",
action: 'meeting_ended' action: 'meeting_ended'
break break
end end

View File

@ -17,8 +17,8 @@
class JoinMeetingJob < ApplicationJob class JoinMeetingJob < ApplicationJob
queue_as :default queue_as :default
def perform(room) def perform(room, meeting)
ActionCable.server.broadcast "#{room}_meeting_updates_channel", ActionCable.server.broadcast "#{room}-#{meeting}_meeting_updates_channel",
action: 'moderator_joined', action: 'moderator_joined',
moderator: 'joined' moderator: 'joined'
end end

View File

@ -17,8 +17,8 @@
class NotifyUserWaitingJob < ApplicationJob class NotifyUserWaitingJob < ApplicationJob
queue_as :default queue_as :default
def perform(room, user) def perform(room, meeting, user)
ActionCable.server.broadcast "#{room}_meeting_updates_channel", ActionCable.server.broadcast "#{room}-#{meeting}_meeting_updates_channel",
{ action: 'user_waiting', user: user } { action: 'user_waiting', user: user }
end end
end end

View File

@ -8,6 +8,8 @@
<div class="verticle-line"></div> <div class="verticle-line"></div>
<div class="col-xs-6"> <div class="col-xs-6">
<%= render 'shared/title', title: t('join'), title_class: 'join-meeting-title' %> <%= render 'shared/title', title: t('join'), title_class: 'join-meeting-title' %>
<%= render 'shared/join_form' %> <button type="button" class="btn btn-primary center-block meeting-start">
<%= t('start_meeting') %>
</button>
</div> </div>
</div> </div>

View File

@ -31,18 +31,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<div class="center-block center-panel-content-size col-xs-12"> <div class="center-block center-panel-content-size col-xs-12">
<% if admin? %> <% if admin? %>
<%= render 'shared/meeting_name_form' %> <%= render 'shared/meeting_name_form' %>
<div class="invite-join-wrapper"> <div class="row">
<div class="col-xs-6"> <%= render 'landing/invite_join' %>
<%= render 'shared/title', title: t('invite') do %>
<span><%= t('invite_description') %></span>
<% end %>
<%= render 'shared/meeting_url', hidden: false %>
</div>
<div class="verticle-line"></div>
<div class="col-xs-6">
<%= render 'shared/title', title: t('join'), title_class: 'join-meeting-title' %>
<%= render 'shared/join_form' %>
</div>
</div> </div>
<% else %> <% else %>
<%= render 'shared/meeting_url', hidden: true %> <%= render 'shared/meeting_url', hidden: true %>

View File

@ -0,0 +1,29 @@
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<% content_for :title do %>
<div class="title">
<h2>
<%= t('join_session_id', id: @meeting_id) %>
</h2>
</div>
<% end %>
<%= render layout: 'shared/center_panel' do %>
<div class="center-block center-panel-content-size col-xs-12">
<%= render 'shared/meeting_url', hidden: true %>
<%= render 'shared/join_form' %>
</div>
<% end %>

View File

@ -19,11 +19,15 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<% page_title= t('greet_guest', name: @user.username) %> <% page_title= t('greet_guest', name: @user.username) %>
<% end %> <% end %>
<div class="page-wrapper rooms" data-id="<%= @user.encrypted_id %>"> <div class="page-wrapper rooms" data-main-room="<%= @main_room %>" data-id="<%= @meeting_id %>" data-admin-id="<%= @user.encrypted_id %>">
<div class="container-fluid"> <div class="container-fluid">
<div class="center-panel-wrapper"> <div class="center-panel-wrapper">
<% if @main_room %>
<%= render 'rooms_center_panel' %> <%= render 'rooms_center_panel' %>
<% else %>
<%= render 'rooms_meetings_center_panel' %>
<% end %>
</div> </div>
<%= render 'shared/signup' %> <%= render 'shared/signup' %>

View File

@ -13,4 +13,4 @@ You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
--> -->
<%= render 'rooms_center_panel' %> <%= render 'rooms_meetings_center_panel' %>

View File

@ -30,14 +30,17 @@ Rails.application.routes.draw do
# rooms offer a customized landing page for authenticated users to create and join session in BigBlueButton # rooms offer a customized landing page for authenticated users to create and join session in BigBlueButton
get '/:resource/:id', to: 'landing#resource', as: :resource get '/:resource/:id', to: 'landing#resource', as: :resource
get '/:resource/:id/join', to: 'bbb#join', as: :bbb_join, defaults: {format: 'json'} get '/:resource/:id/join', to: 'bbb#join', as: :bbb_join, defaults: {format: 'json'}
get '/:resource/:id/wait', to: 'landing#wait_for_moderator'
get '/:resource/:id/session_status_refresh', to: 'landing#session_status_refresh' get '/rooms/:room_id/recordings', to: 'bbb#recordings', defaults: {format: 'json'}
post '/:resource/:id/callback', to: 'bbb#callback' #, defaults: {format: 'json'} post '/:resource/:id/callback', to: 'bbb#callback' #, defaults: {format: 'json'}
delete '/rooms/:id/end', to: 'bbb#end', defaults: {format: 'json'}
get '/:resource/:room_id/:id/join', to: 'bbb#join', defaults: {format: 'json'}
get '/rooms/:id/recordings', to: 'bbb#recordings', defaults: {format: 'json'}
patch '/rooms/:id/recordings/:record_id', to: 'bbb#update_recordings', defaults: {format: 'json'} patch '/rooms/:id/recordings/:record_id', to: 'bbb#update_recordings', defaults: {format: 'json'}
delete '/rooms/:id/recordings/:record_id', to: 'bbb#delete_recordings', defaults: {format: 'json'} delete '/rooms/:id/recordings/:record_id', to: 'bbb#delete_recordings', defaults: {format: 'json'}
get '/:resource/:room_id/:id/wait', to: 'landing#wait_for_moderator'
get '/:resource/:room_id/:id/session_status_refresh', to: 'landing#session_status_refresh'
delete '/rooms/:room_id/:id/end', to: 'bbb#end', defaults: {format: 'json'}
get '/rooms/:room_id/:id', to: 'landing#resource', resource: 'rooms'
get '/:resource/:room_id/:id/join', to: 'bbb#join', defaults: {format: 'json'}
root to: 'landing#index', :resource => "meetings"
root to: 'landing#index', :resource => 'meetings'
end end

View File

@ -39,12 +39,12 @@ class LandingControllerTest < ActionController::TestCase
end end
test "should get wait for moderator" do test "should get wait for moderator" do
get :wait_for_moderator, params: { id: @user.encrypted_id, resource: 'rooms' } get :wait_for_moderator, params: { room_id: @user.encrypted_id, id: 'room1', resource: 'rooms' }
assert_response :success assert_response :success
end end
test "should get session status refresh" do test "should get session status refresh" do
get :wait_for_moderator, params: { id: @user.encrypted_id, resource: 'rooms' } get :wait_for_moderator, params: { room_id: @user.encrypted_id, id: 'room1', resource: 'rooms' }
assert_response :success assert_response :success
end end