forked from External/greenlight
		
	add support for BBB 2.0 webhooks
This commit is contained in:
		@@ -31,22 +31,41 @@ var updatePreviousMeetings = function(){
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ignore excess on either side of user_id.
 | 
				
			||||||
 | 
					var trimUserId = function(user_id){
 | 
				
			||||||
 | 
					  components = user_id.split('_')
 | 
				
			||||||
 | 
					  return components.sort(function (a, b) {return b.length - a.length;})[0]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Finds a user by their user_id.
 | 
				
			||||||
 | 
					var findByUserId = function(users, user_id){
 | 
				
			||||||
 | 
					  for(i = 0; i < users.length; i++){
 | 
				
			||||||
 | 
					    if(trimUserId(users[i]['user_id']) == trimUserId(user_id)){
 | 
				
			||||||
 | 
					      return i
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return undefined
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Adds a user to a meeting.
 | 
					// Adds a user to a meeting.
 | 
				
			||||||
var addUser = function(data){
 | 
					var addUser = function(data){
 | 
				
			||||||
  if(data['role'] == 'MODERATOR'){
 | 
					  if(data['role'] == 'MODERATOR'){
 | 
				
			||||||
    MEETINGS[data['meeting']]['moderators'].push(data['user'])
 | 
					    MEETINGS[data['meeting']]['moderators'].push({'name': data['user'], 'user_id': data['user_id']})
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    MEETINGS[data['meeting']]['participants'].push(data['user'])
 | 
					    MEETINGS[data['meeting']]['participants'].push({'name':data['user'], 'user_id': data['user_id']})
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  updateMeetingText(MEETINGS[data['meeting']])
 | 
					  updateMeetingText(MEETINGS[data['meeting']])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Removes a user from a meeting.
 | 
					// Removes a user from a meeting.
 | 
				
			||||||
var removeUser = function(data){
 | 
					var removeUser = function(data){
 | 
				
			||||||
  if(data['role'] == 'MODERATOR'){
 | 
					  user = findByUserId(MEETINGS[data['meeting']]['moderators'], data['user_id'])
 | 
				
			||||||
    MEETINGS[data['meeting']]['moderators'].splice(MEETINGS[data['meeting']]['moderators'].indexOf(data['user']), 1);
 | 
					  if(user == undefined){
 | 
				
			||||||
 | 
					    user = findByUserId(MEETINGS[data['meeting']]['participants'], data['user_id']);
 | 
				
			||||||
 | 
					    if(user == undefined){ return; }
 | 
				
			||||||
 | 
					    MEETINGS[data['meeting']]['participants'].splice(user, 1);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    MEETINGS[data['meeting']]['participants'].splice(MEETINGS[data['meeting']]['participants'].indexOf(data['user']), 1);
 | 
					    MEETINGS[data['meeting']]['moderators'].splice(user, 1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  updateMeetingText(MEETINGS[data['meeting']])
 | 
					  updateMeetingText(MEETINGS[data['meeting']])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -60,14 +79,15 @@ var updateMeetingText = function(m){
 | 
				
			|||||||
    if(m['moderators'].length + m['participants'].length == 0){
 | 
					    if(m['moderators'].length + m['participants'].length == 0){
 | 
				
			||||||
      list = '(empty)'
 | 
					      list = '(empty)'
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      list = m['moderators'].join('(mod), ') + (m['moderators'].length > 0 ? '(mod)' : '') +
 | 
					      list = m['moderators'].map(function(x){ return x['name']; }).join('(mod), ') +
 | 
				
			||||||
        (m['participants'].length > 0 && m['moderators'].length != 0 ? ', ' : '') + m['participants'].join(', ')
 | 
					        (m['moderators'].length > 0 ? '(mod)' : '') +
 | 
				
			||||||
 | 
					        (m['participants'].length > 0 && m['moderators'].length != 0 ? ', ' : '') +
 | 
				
			||||||
 | 
					        (m['participants'].map(function(x){ return x['name']; }).join(', '))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    body = '<a>' + m['name'] + '</a><i>: ' + list + '</i>'
 | 
					    body = '<a>' + m['name'] + '</a><i>: ' + list + '</i>'
 | 
				
			||||||
  // Otherwise it hasn't started (users waiting the join).
 | 
					  // Otherwise it hasn't started (users waiting the join).
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    body = '<a>' + m['name'] + '</a><i> (not yet started): ' + 
 | 
					    body = '<a>' + m['name'] + '</a><i> (not yet started): ' +  m['users'].join(', ') + '</i>'
 | 
				
			||||||
                  m['users'].join(', ') + '</i>'
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // If the item doesn't exist, add it and set up join meeting event.
 | 
					  // If the item doesn't exist, add it and set up join meeting event.
 | 
				
			||||||
@@ -91,65 +111,41 @@ var initialPopulate = function(){
 | 
				
			|||||||
  // Only populate on room resources.
 | 
					  // Only populate on room resources.
 | 
				
			||||||
  var chopped = window.location.href.split('/')
 | 
					  var chopped = window.location.href.split('/')
 | 
				
			||||||
  if (!window.location.href.includes('rooms') || chopped[chopped.length - 2] == $('body').data('current-user')) { return; }
 | 
					  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['active']['meetings']
 | 
					 | 
				
			||||||
    var waiting = data['waiting']
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
    jQuery.each(waiting[$('body').data('current-user')], function(name, users){
 | 
					  $.post((window.location.href + '/statuses').replace('#', ''), {previously_joined: getPreviouslyJoined()})
 | 
				
			||||||
      WAITING[name] = {'name': name,
 | 
					    .done(function(data) {
 | 
				
			||||||
                       'users': users}
 | 
					 | 
				
			||||||
      updateMeetingText(WAITING[name])
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    for(var i = 0; i < meetings.length; i++){
 | 
					      // Populate waiting meetings.
 | 
				
			||||||
      // Make sure the meeting actually belongs to the current user.
 | 
					      Object.keys(data['waiting']).forEach(function(key) { 
 | 
				
			||||||
      if(meetings[i]['metadata']['room-id'] != $('body').data('current-user')) { continue; }
 | 
					        WAITING[name] = {'name': key, 'users': data['waiting'][key]}
 | 
				
			||||||
      var name = meetings[i]['meetingName']
 | 
					        updateMeetingText(WAITING[name])
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      var attendees;
 | 
					      // Add the meetings to the active meetings list.
 | 
				
			||||||
      if(meetings[i]['attendees']['attendee'] instanceof Array){
 | 
					      for(var i = 0; i < data['active'].length; i++){
 | 
				
			||||||
        attendees = meetings[i]['attendees']['attendee']
 | 
					        var meeting = data['active'][i]
 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        attendees = [meetings[i]['attendees']['attendee']]
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
      var participants = []
 | 
					        var name = meeting['name']
 | 
				
			||||||
      var moderators = []
 | 
					        var participants = meeting['participants']
 | 
				
			||||||
 | 
					        var moderators = meeting['moderators']
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
      jQuery.each(attendees, function(i, attendee){
 | 
					        // Create meeting.
 | 
				
			||||||
        // The API doesn't return a empty array when empty, just undefined.
 | 
					        MEETINGS[name] = {'name': name, 'participants': participants, 'moderators': moderators}            
 | 
				
			||||||
        if(attendee != undefined){
 | 
					 | 
				
			||||||
          if(attendee['role'] == "MODERATOR"){
 | 
					 | 
				
			||||||
            moderators.push(attendee['fullName'])
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            participants.push(attendee['fullName'])
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      
 | 
					 | 
				
			||||||
      // Create meeting.
 | 
					 | 
				
			||||||
      MEETINGS[name] = {'name': name,
 | 
					 | 
				
			||||||
                        'participants': participants,
 | 
					 | 
				
			||||||
                        'moderators': moderators}
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
      if(isPreviouslyJoined(name)){
 | 
					 | 
				
			||||||
        updateMeetingText(MEETINGS[name])
 | 
					        updateMeetingText(MEETINGS[name])
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
  }).done(function(){
 | 
					      // Remove from previous meetings if they are active.
 | 
				
			||||||
    // Remove from previous meetings if they are active.
 | 
					      updatePreviousMeetings();
 | 
				
			||||||
    updatePreviousMeetings();
 | 
					      $('.hidden-list').show();
 | 
				
			||||||
    $('.hidden-list').show();
 | 
					      $('.active-spinner').hide();
 | 
				
			||||||
    $('.active-spinner').hide();
 | 
					    });
 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Checks if a meeting has been prveiously joined by the user.
 | 
					// Gets a list of known previously joined meetings.
 | 
				
			||||||
var isPreviouslyJoined = function(meeting){
 | 
					var getPreviouslyJoined = function(){
 | 
				
			||||||
  var joinedMeetings = localStorage.getItem('joinedRooms-' + $('body').data('current-user'));
 | 
					  var joinedMeetings = localStorage.getItem('joinedRooms-' + $('body').data('current-user'));
 | 
				
			||||||
  if (joinedMeetings == '' || joinedMeetings == null){ return false; }
 | 
					  if (joinedMeetings == '' || joinedMeetings == null){ return []; }
 | 
				
			||||||
  return joinedMeetings.split(',').indexOf(meeting) >= 0
 | 
					  return joinedMeetings.split(',')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Removes an active meeting.
 | 
					// Removes an active meeting.
 | 
				
			||||||
@@ -183,7 +179,6 @@ var joinMeeting = function(meeting_name){
 | 
				
			|||||||
// Only need to register for logged in users.
 | 
					// Only need to register for logged in users.
 | 
				
			||||||
$(document).on('turbolinks:load', function(){
 | 
					$(document).on('turbolinks:load', function(){
 | 
				
			||||||
  if($('body').data('current-user')){
 | 
					  if($('body').data('current-user')){
 | 
				
			||||||
 | 
					 | 
				
			||||||
    MEETINGS = {}
 | 
					    MEETINGS = {}
 | 
				
			||||||
    // Ensure actives is empty.
 | 
					    // Ensure actives is empty.
 | 
				
			||||||
    $('.actives').empty();
 | 
					    $('.actives').empty();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -259,7 +259,12 @@ class BbbController < ApplicationController
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def treat_callback_event(event)
 | 
					  def treat_callback_event(event)
 | 
				
			||||||
    eventName = (event.present? && event['header'].present?) ? event['header']['name'] : nil
 | 
					    # Check if the event is a BigBlueButton 2.0 event.
 | 
				
			||||||
 | 
					    if event.has_key?('envelope')
 | 
				
			||||||
 | 
					      eventName = (event.present? && event['envelope'].present?) ? event['envelope']['name'] : nil
 | 
				
			||||||
 | 
					    else # The event came from BigBlueButton 1.1 (or earlier).
 | 
				
			||||||
 | 
					      eventName = (event.present? && event['header'].present?) ? event['header']['name'] : nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # a recording is ready
 | 
					    # a recording is ready
 | 
				
			||||||
    if eventName == "publish_ended"
 | 
					    if eventName == "publish_ended"
 | 
				
			||||||
@@ -286,11 +291,11 @@ class BbbController < ApplicationController
 | 
				
			|||||||
      else
 | 
					      else
 | 
				
			||||||
        logger.error "Bad format for event #{event}, won't process"
 | 
					        logger.error "Bad format for event #{event}, won't process"
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    elsif eventName == "meeting_created_message"
 | 
					    elsif eventName == "meeting_created_message" || eventName == "MeetingCreatedEvtMsg" 
 | 
				
			||||||
      # Fire an Actioncable event that updates _previously_joined for the client.
 | 
					      # Fire an Actioncable event that updates _previously_joined for the client.
 | 
				
			||||||
      actioncable_event('create', params[:id], params[:room_id])
 | 
					      actioncable_event('create')
 | 
				
			||||||
    elsif eventName == "meeting_destroyed_event"
 | 
					    elsif eventName == "meeting_destroyed_event" || eventName == "MeetingEndedEvtMsg"
 | 
				
			||||||
      actioncable_event('destroy', params[:id], params[:room_id])
 | 
					      actioncable_event('destroy')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Since the meeting is destroyed we have no way get the callback url to remove the meeting, so we must build it.
 | 
					      # Since the meeting is destroyed we have no way get the callback url to remove the meeting, so we must build it.
 | 
				
			||||||
      remove_url = build_callback_url(params[:id], params[:room_id])
 | 
					      remove_url = build_callback_url(params[:id], params[:room_id])
 | 
				
			||||||
@@ -298,9 +303,13 @@ class BbbController < ApplicationController
 | 
				
			|||||||
      # Remove webhook for the meeting.
 | 
					      # Remove webhook for the meeting.
 | 
				
			||||||
      webhook_remove(remove_url)
 | 
					      webhook_remove(remove_url)
 | 
				
			||||||
    elsif eventName == "user_joined_message"
 | 
					    elsif eventName == "user_joined_message"
 | 
				
			||||||
      actioncable_event('join', params[:id], params[:room_id], event['payload']['user']['name'], event['payload']['user']['role'])
 | 
					      actioncable_event('join', {user_id: event['payload']['user']['extern_userid'], user: event['payload']['user']['name'], role: event['payload']['user']['role']})
 | 
				
			||||||
 | 
					    elsif eventName == "UserJoinedMeetingEvtMsg"
 | 
				
			||||||
 | 
					      actioncable_event('join', {user_id: event['core']['body']['intId'], user: event['core']['body']['name'], role: event['core']['body']['role']})
 | 
				
			||||||
    elsif eventName == "user_left_message"
 | 
					    elsif eventName == "user_left_message"
 | 
				
			||||||
      actioncable_event('leave', params[:id], params[:room_id], event['payload']['user']['name'], event['payload']['user']['role'])
 | 
					      actioncable_event('leave', {user_id: event['payload']['user']['extern_userid']})
 | 
				
			||||||
 | 
					    elsif eventName == "UserLeftMeetingEvtMsg"
 | 
				
			||||||
 | 
					      actioncable_event('leave', {user_id: event['core']['body']['intId']})
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      logger.info "Callback event will not be treated. Event name: #{eventName}"
 | 
					      logger.info "Callback event will not be treated. Event name: #{eventName}"
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -312,13 +321,9 @@ class BbbController < ApplicationController
 | 
				
			|||||||
    "#{request.base_url}#{relative_root}/rooms/#{room_id}/#{URI.encode(id)}/callback"
 | 
					    "#{request.base_url}#{relative_root}/rooms/#{room_id}/#{URI.encode(id)}/callback"
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def actioncable_event(method, id, room_id, user = 'none', role = 'none')
 | 
					  def actioncable_event(method, data = {})
 | 
				
			||||||
    ActionCable.server.broadcast 'refresh_meetings',
 | 
					    data = {method: method, meeting: params[:id], room: params[:room_id]}.merge(data)
 | 
				
			||||||
      method: method,
 | 
					    ActionCable.server.broadcast('refresh_meetings', data)
 | 
				
			||||||
      meeting: id,
 | 
					 | 
				
			||||||
      room: room_id,
 | 
					 | 
				
			||||||
      user: user,
 | 
					 | 
				
			||||||
      role: role
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Validates the checksum received in a callback call.
 | 
					  # Validates the checksum received in a callback call.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,8 +45,37 @@ class LandingController < ApplicationController
 | 
				
			|||||||
    redirect_to root_url unless Rails.configuration.disable_guest_access
 | 
					    redirect_to root_url unless Rails.configuration.disable_guest_access
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  def send_meetings_data
 | 
					  # Sends data on meetings that the current user has previously joined.
 | 
				
			||||||
    render json: {active: bbb.get_meetings, waiting: WaitingList.waiting}
 | 
					  def get_previous_meeting_statuses
 | 
				
			||||||
 | 
					    previously_joined = params[:previously_joined]
 | 
				
			||||||
 | 
					    active_meetings = bbb.get_meetings[:meetings]
 | 
				
			||||||
 | 
					    payload = {active: [], waiting: []}
 | 
				
			||||||
 | 
					    # Find meetings that are owned by the current user and also active.
 | 
				
			||||||
 | 
					    active_meetings.each do |m|
 | 
				
			||||||
 | 
					      if m[:metadata].has_key?(:'room-id')
 | 
				
			||||||
 | 
					        if previously_joined.include?(m[:meetingName])&& m[:metadata][:'room-id'] == current_user[:encrypted_id]
 | 
				
			||||||
 | 
					          if m[:attendees] == {}
 | 
				
			||||||
 | 
					            attendees = []
 | 
				
			||||||
 | 
					          else
 | 
				
			||||||
 | 
					            attendees = m[:attendees][:attendee]
 | 
				
			||||||
 | 
					            attendees = [attendees] unless attendees.is_a?(Array)
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					          participants = []
 | 
				
			||||||
 | 
					          moderators = []
 | 
				
			||||||
 | 
					          attendees.each do |a|
 | 
				
			||||||
 | 
					            if a[:role] == 'MODERATOR'
 | 
				
			||||||
 | 
					              moderators << {name: a[:fullName], user_id: a[:userID]}
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					              participants << {name: a[:fullName], user_id: a[:userID]}
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					          payload[:active] << {name: m[:meetingName], moderators: moderators, participants: participants}
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end  
 | 
				
			||||||
 | 
					    # Add the waiting meetings.
 | 
				
			||||||
 | 
					    payload[:waiting] = WaitingList.waiting[current_user[:encrypted_id]] || {}
 | 
				
			||||||
 | 
					    render json: payload
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def wait_for_moderator
 | 
					  def wait_for_moderator
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,7 +51,7 @@ Rails.application.routes.draw do
 | 
				
			|||||||
    post '/:room_id/:id/callback', to: 'bbb#callback', :constraints => {:id => disallow_slash, :room_id => disallow_slash}
 | 
					    post '/:room_id/:id/callback', to: 'bbb#callback', :constraints => {:id => disallow_slash, :room_id => disallow_slash}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # routes shared between meetings and rooms
 | 
					    # routes shared between meetings and rooms
 | 
				
			||||||
    get '/(:room_id)/request', to: 'landing#send_meetings_data', :defaults => { :format => 'xml' }
 | 
					    post '/(:room_id)/statuses', to: 'landing#get_previous_meeting_statuses'
 | 
				
			||||||
    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/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}
 | 
					    get '/(:room_id)/:id', to: 'landing#resource', as: :meeting_room, defaults: {room_id: nil}, :constraints => {:id => disallow_slash, :room_id => disallow_slash}
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user