diff --git a/app/assets/javascripts/active_meetings.js b/app/assets/javascripts/active_meetings.js
index c7b92bb4..e3308791 100644
--- a/app/assets/javascripts/active_meetings.js
+++ b/app/assets/javascripts/active_meetings.js
@@ -18,6 +18,7 @@
// the landing page using custom Actioncable events.
var MEETINGS = {}
+var WAITING = {}
var LOADING_DELAY = 1750 // milliseconds.
var updatePreviousMeetings = function(){
@@ -29,19 +30,44 @@ var updatePreviousMeetings = function(){
});
}
-var handleUser = function(data, n){
+var addUser = function(data){
if(data['role'] == 'MODERATOR'){
- MEETINGS[data['meeting']]['moderators'] += n
+ MEETINGS[data['meeting']]['moderators'].push(data['user'])
} else {
- MEETINGS[data['meeting']]['participants'] += n
+ MEETINGS[data['meeting']]['participants'].push(data['user'])
}
updateMeetingText(MEETINGS[data['meeting']])
}
-var updateMeetingText = function(meeting){
- $('#' + meeting['name'].replace(' ', '_')).html('' + meeting['name'] + ' (' +
- meeting['participants'] + ((meeting['participants'] == 1) ? ' user, ' : ' users, ') +
- meeting['moderators'] + ((meeting['moderators'] == 1) ? ' mod)' : ' mods)'))
+var removeUser = function(data){
+ if(data['role'] == 'MODERATOR'){
+ MEETINGS[data['meeting']]['moderators'].splice(MEETINGS[data['meeting']]['moderators'].indexOf(data['user']), 1);
+ } else {
+ MEETINGS[data['meeting']]['participants'].splice(MEETINGS[data['meeting']]['participants'].indexOf(data['user']), 1);
+ }
+ updateMeetingText(MEETINGS[data['meeting']])
+}
+
+var updateMeetingText = function(m){
+ if(m.hasOwnProperty('moderators')){
+ var body = '' + m['name'] + ': ' + m['moderators'].join('(mod), ') + (m['moderators'].length > 0 ? '(mod)' : '') +
+ (m['participants'].length > 0 && m['moderators'].length != 0 ? ', ' : '') + m['participants'].join(', ') + ''
+ } else {
+ var body = '' + m['name'] + ' (not yet started): ' +
+ m['users'].join(', ') + ''
+ }
+
+ if($('#' + m['name'].replace(' ', '_')).length == 0){
+ var meeting_item = $('' + body + '')
+ $('.actives').append(meeting_item);
+
+ // Set up join on click.
+ meeting_item.click(function(){
+ joinMeeting(m['name']);
+ });
+ } else {
+ $('#' + m['name'].replace(' ', '_')).html(body)
+ }
}
var initialPopulate = function(){
@@ -49,21 +75,48 @@ var initialPopulate = function(){
var chopped = window.location.href.split('/')
if (!window.location.href.includes('rooms') || chopped[chopped.length - 2] == $('body').data('current-user')) { return; }
$.get((window.location.href + '/request').replace('#', ''), function(data){
- var meetings = data['meetings']
+ var meetings = data['active']['meetings']
+ var waiting = data['waiting']
+
+ jQuery.each(waiting[$('body').data('current-user')], function(name, users){
+ WAITING[name] = {'name': name,
+ 'users': users}
+ updateMeetingText(WAITING[name])
+ });
+
for(var i = 0; i < meetings.length; i++){
// Make sure the meeting actually belongs to the current user.
if(meetings[i]['metadata']['room-id'] != $('body').data('current-user')) { continue; }
var name = meetings[i]['meetingName']
- var participants = parseInt(meetings[i]['participantCount'])
- var moderators = parseInt(meetings[i]['moderatorCount'])
+
+ var participants = []
+ var moderators = []
+
+ var attendees;
+ if(meetings[i]['attendees']['attendee'] instanceof Array){
+ attendees = meetings[i]['attendees']['attendee']
+ } else {
+ attendees = [meetings[i]['attendees']['attendee']]
+ }
+
+ jQuery.each(attendees, function(i, attendee){
+ if(attendee['role'] == "MODERATOR"){
+ moderators.push(attendee['fullName'])
+ } else {
+ participants.push(attendee['fullName'])
+ }
+ });
+
// Create meeting.
MEETINGS[name] = {'name': name,
- 'participants': participants - moderators,
+ 'participants': participants,
'moderators': moderators}
+
if(isPreviouslyJoined(name)){
- renderActiveMeeting(MEETINGS[name])
+ updateMeetingText(MEETINGS[name])
}
}
+
}).done(function(){
// Remove from previous meetings if they are active.
updatePreviousMeetings();
@@ -81,18 +134,6 @@ var isPreviouslyJoined = function(meeting){
return joinedMeetings.split(',').indexOf(meeting) >= 0
}
-var renderActiveMeeting = function(m){
- var meeting_item = $('' + m['name'] + '' +
- ' (' + m['participants'] + ((m['participants'] == 1) ? ' user, ' : ' users, ') +
- m['moderators'] + ((m['moderators'] == 1) ? ' mod)' : ' mods)') + '' + '')
- $('.actives').append(meeting_item);
-
- // Set up join on click.
- meeting_item.click(function(){
- joinMeeting(m['name']);
- });
-}
-
var removeActiveMeeting = function(meeting){
if(meeting){
$('#' + meeting['name'].replace(' ', '_')).remove()
@@ -137,20 +178,36 @@ $(document).on('turbolinks:load', function(){
if(data['method'] == 'create'){
// Create an empty meeting.
MEETINGS[data['meeting']] = {'name': data['meeting'],
- 'participants': 0,
- 'moderators': 0}
-
- renderActiveMeeting(MEETINGS[data['meeting']])
+ 'participants': [],
+ 'moderators': []}
+ updateMeetingText(MEETINGS[data['meeting']])
updatePreviousMeetings();
+ if (WAITING.hasOwnProperty(data['meeting'])){ delete WAITING[data['meeting']]; }
} else if(data['method'] == 'destroy'){
removeActiveMeeting(MEETINGS[data['meeting']])
PreviousMeetings.uniqueAdd([data['meeting']])
delete MEETINGS[data['meeting']]
} else if(data['method'] == 'join'){
- handleUser(data, 1)
- updateMeetingText(MEETINGS[data['meeting']])
+ addUser(data)
} else if(data['method'] == 'leave'){
- handleUser(data, -1)
+ removeUser(data)
+ } else if(data['method'] == 'waiting'){
+ // Handle waiting meeting.
+ if(WAITING.hasOwnProperty(data['meeting'])){
+ WAITING[data['meeting']]['users'].push(data['user'])
+ updateMeetingText(WAITING[data['meeting']])
+ } else {
+ WAITING[data['meeting']] = {'name': data['meeting'],
+ 'users': [data['user']]}
+ updateMeetingText(WAITING[data['meeting']])
+ }
+ } else if((data['method'] == 'no_longer_waiting') && (WAITING.hasOwnProperty(data['meeting']))){
+ WAITING[data['meeting']]['users'].splice(WAITING[data['meeting']]['users'].indexOf(data['user']), 1)
+ updateMeetingText(WAITING[data['meeting']])
+ if(WAITING[data['meeting']]['users'].length == 0){
+ removeActiveMeeting(WAITING[data['meeting']])
+ delete WAITING[data['meeting']]
+ }
}
}
}
diff --git a/app/assets/javascripts/landing.js b/app/assets/javascripts/landing.js
index 7f293a9b..46759da1 100644
--- a/app/assets/javascripts/landing.js
+++ b/app/assets/javascripts/landing.js
@@ -17,7 +17,8 @@
(function() {
var waitForModerator = function(url) {
- $.get(url + "/wait", function(html) {
+ localStorage.setItem("waitingName", $('.meeting-user-name').val());
+ $.post(url + "/wait", {name: $('.meeting-user-name').val()}, function(html) {
$(".center-panel-wrapper").html(html);
});
if (!Meeting.getInstance().getWaitingForMod()) {
diff --git a/app/controllers/bbb_controller.rb b/app/controllers/bbb_controller.rb
index e9fe764c..2b74290b 100644
--- a/app/controllers/bbb_controller.rb
+++ b/app/controllers/bbb_controller.rb
@@ -287,14 +287,17 @@ class BbbController < ApplicationController
actioncable_event('create', params['id'], params['room_id'])
elsif eventName == "meeting_destroyed_event"
actioncable_event('destroy', params['id'], params['room_id'])
+
record_id = event['payload']['meeting_id'].split('-')[0]
rec_info = bbb_get_recordings({recordID: record_id})
rec_info = rec_info[:recordings].first
+
+ # Remove the webhook.
webhook_remove(rec_info[:metadata][:"gl-webhooks-callback-url"])
elsif eventName == "user_joined_message"
- actioncable_event('join', params['id'], params['room_id'], event['payload']['user']['role'])
+ actioncable_event('join', params['id'], params['room_id'], event['payload']['user']['name'], event['payload']['user']['role'])
elsif eventName == "user_left_message"
- actioncable_event('leave', params['id'], params['room_id'], event['payload']['user']['role'])
+ actioncable_event('leave', params['id'], params['room_id'], event['payload']['user']['name'], event['payload']['user']['role'])
else
logger.info "Callback event will not be treated. Event name: #{eventName}"
end
@@ -302,11 +305,12 @@ class BbbController < ApplicationController
render head(:ok) && return
end
- def actioncable_event(method, id, room_id, role = 'none')
+ def actioncable_event(method, id, room_id, user = 'none', role = 'none')
ActionCable.server.broadcast 'refresh_meetings',
method: method,
meeting: id,
room: room_id,
+ user: user,
role: role
end
diff --git a/app/controllers/landing_controller.rb b/app/controllers/landing_controller.rb
index 4f67eec3..58baeeac 100644
--- a/app/controllers/landing_controller.rb
+++ b/app/controllers/landing_controller.rb
@@ -34,14 +34,29 @@ class LandingController < ApplicationController
end
end
- def send_data
- render json: bbb.get_meetings
+ def send_meetings_data
+ render json: {active: bbb.get_meetings, waiting: WaitingList.waiting}
end
def wait_for_moderator
+ WaitingList.add(params[:room_id], params[:name], params[:id])
+ ActionCable.server.broadcast 'refresh_meetings',
+ method: 'waiting',
+ meeting: params[:id],
+ room: params[:room_id],
+ user: params[:name]
render layout: false
end
+ def no_longer_waiting
+ WaitingList.remove(params[:room_id], params[:name], params[:id])
+ ActionCable.server.broadcast 'refresh_meetings',
+ method: 'no_longer_waiting',
+ meeting: params[:id],
+ room: params[:room_id],
+ user: params[:name]
+ end
+
def session_status_refresh
@user = User.find_by(encrypted_id: params[:room_id])
if @user.nil?
diff --git a/app/lib/bbb_api.rb b/app/lib/bbb_api.rb
index f68a7897..420c5747 100644
--- a/app/lib/bbb_api.rb
+++ b/app/lib/bbb_api.rb
@@ -100,6 +100,8 @@ module BbbApi
rescue BigBlueButton::BigBlueButtonException => exc
logger.info "BBB error on create #{exc.key}: #{exc.message}"
end
+
+ WaitingList.empty(options[:room_owner], options[:meeting_name])
# And then get meeting info
bbb_meeting_info = bbb.get_meeting_info( meeting_id, nil )
diff --git a/app/views/landing/wait_for_moderator.html.erb b/app/views/landing/wait_for_moderator.html.erb
index 8d161c27..ba6156f5 100644
--- a/app/views/landing/wait_for_moderator.html.erb
+++ b/app/views/landing/wait_for_moderator.html.erb
@@ -28,3 +28,11 @@
<%= image_tag "loading-indicator.gif", :alt => "" %>
<% end %>
+
+
diff --git a/config/initializers/waiting_list.rb b/config/initializers/waiting_list.rb
new file mode 100644
index 00000000..a3c536b4
--- /dev/null
+++ b/config/initializers/waiting_list.rb
@@ -0,0 +1,35 @@
+# Stores data on waiting users on the server side so
+# we can pass it to clients when they reload the page.
+
+class WaitingList
+ @waiting = {}
+
+ def self.waiting
+ @waiting
+ end
+
+ def self.add(room, user, meeting)
+ @waiting[room] = {} unless @waiting.has_key?(room)
+ @waiting[room][meeting] = [] unless @waiting[room].has_key?(meeting)
+ @waiting[room][meeting] << user
+ end
+
+ def self.remove(room, user, meeting)
+ if @waiting.has_key?(room) then
+ if @waiting[room].has_key?(meeting) then
+ @waiting[room][meeting].slice!(@waiting[room][meeting].index(user))
+ @waiting[room].delete(meeting) if @waiting[room][meeting].length == 0
+ @waiting.delete(room) if @waiting[room].length == 0
+ end
+ end
+ end
+
+ def self.empty(room, meeting)
+ if @waiting.has_key?(room) then
+ if @waiting[room].has_key?(meeting) then
+ @waiting[room].delete(meeting)
+ @waiting.delete(room) if @waiting[room].length == 0
+ end
+ end
+ end
+end
diff --git a/config/routes.rb b/config/routes.rb
index 68166c0c..1bf019f7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -44,13 +44,14 @@ Rails.application.routes.draw do
delete '/(:id)/recordings/:record_id', to: 'bbb#delete_recordings', defaults: {id: nil, format: 'json'}, :constraints => {:id => disallow_slash}
delete '/:id/end', to: 'bbb#end', defaults: {format: 'json'}, :constraints => {:id => disallow_slash}
- get '/:id/wait', to: 'landing#wait_for_moderator', :constraints => {:id => disallow_slash}
+ post '/:id/wait', to: 'landing#wait_for_moderator', :constraints => {:id => disallow_slash}
+ post '/:id/no_longer_wait', to: 'landing#no_longer_waiting', :constraints => {:id => disallow_slash}
get '/:id/session_status_refresh', to: 'landing#session_status_refresh', :constraints => {:id => disallow_slash}
end
post '/:room_id/:id/callback', to: 'bbb#callback', :constraints => {:id => disallow_slash, :room_id => disallow_slash}
# routes shared between meetings and rooms
- get '/(:room_id)/request', to: 'landing#send_data', :defaults => { :format => 'xml' }
+ get '/(:room_id)/request', to: 'landing#send_meetings_data', :defaults => { :format => 'xml' }
get '/(:room_id)/:id/join', to: 'bbb#join', defaults: {room_id: nil, format: 'json'}, :constraints => {:id => disallow_slash, :room_id => disallow_slash}
get '/(:room_id)/:id', to: 'landing#resource', as: :meeting_room, defaults: {room_id: nil}, :constraints => {:id => disallow_slash, :room_id => disallow_slash}
end