Merge pull request #625 from antobinary/lock-settings

html5client:Lock settings implementation
This commit is contained in:
Richard Alam 2015-04-27 10:47:24 -04:00
commit 85d3bca1d1
17 changed files with 236 additions and 60 deletions

View File

@ -20,7 +20,6 @@ public class WhiteboardListener implements MessageHandler{
@Override @Override
public void handleMessage(String pattern, String channel, String message) { public void handleMessage(String pattern, String channel, String message) {
if (channel.equalsIgnoreCase(MessagingConstants.TO_WHITEBOARD_CHANNEL)) { if (channel.equalsIgnoreCase(MessagingConstants.TO_WHITEBOARD_CHANNEL)) {
System.out.println("AntonChannel=(whiteboard)" + channel);
JsonParser parser = new JsonParser(); JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message); JsonObject obj = (JsonObject) parser.parse(message);
@ -44,7 +43,7 @@ public class WhiteboardListener implements MessageHandler{
else { else {
System.out.println("\n DID NOT FIND A whiteboardID \n"); System.out.println("\n DID NOT FIND A whiteboardID \n");
} }
System.out.println("\n\n\n user<" + requesterID + "> requested the shapes.\n\n"); System.out.println("\n user<" + requesterID + "> requested the shapes.\n");
} }
} }
} }

View File

@ -158,6 +158,9 @@ class BigBlueButtonActor(outGW: MessageOutGateway) extends Actor with LogHelper
//send chat history //send chat history
this ! (new GetChatHistoryRequest(id, "nodeJSapp", "nodeJSapp")) this ! (new GetChatHistoryRequest(id, "nodeJSapp", "nodeJSapp"))
//send lock settings
this ! (new GetLockSettings(id, "nodeJSapp"))
} }
outGW.send(new GetAllMeetingsReply(resultArray)) outGW.send(new GetAllMeetingsReply(resultArray))

View File

@ -2,7 +2,6 @@ package org.bigbluebutton.core
import scala.actors.Actor import scala.actors.Actor
import scala.actors.Actor._ import scala.actors.Actor._
import org.bigbluebutton.core.apps.poll.PollApp
import org.bigbluebutton.core.apps.poll.Poll import org.bigbluebutton.core.apps.poll.Poll
import org.bigbluebutton.core.apps.poll.PollApp import org.bigbluebutton.core.apps.poll.PollApp
import org.bigbluebutton.core.apps.users.UsersApp import org.bigbluebutton.core.apps.users.UsersApp
@ -90,6 +89,7 @@ class MeetingActor(val meetingID: String, val externalMeetingID: String, val mee
case msg: MuteUserRequest => handleMuteUserRequest(msg) case msg: MuteUserRequest => handleMuteUserRequest(msg)
case msg: EjectUserFromVoiceRequest => handleEjectUserRequest(msg) case msg: EjectUserFromVoiceRequest => handleEjectUserRequest(msg)
case msg: SetLockSettings => handleSetLockSettings(msg) case msg: SetLockSettings => handleSetLockSettings(msg)
case msg: GetLockSettings => handleGetLockSettings(msg)
case msg: LockUserRequest => handleLockUserRequest(msg) case msg: LockUserRequest => handleLockUserRequest(msg)
case msg: InitLockSettings => handleInitLockSettings(msg) case msg: InitLockSettings => handleInitLockSettings(msg)
case msg: InitAudioSettings => handleInitAudioSettings(msg) case msg: InitAudioSettings => handleInitAudioSettings(msg)

View File

@ -81,18 +81,11 @@ trait UsersApp {
//send the reply //send the reply
outGW.send(new ValidateAuthTokenReply(meetingID, msg.userId, msg.token, true, msg.correlationId, msg.sessionId)) outGW.send(new ValidateAuthTokenReply(meetingID, msg.userId, msg.token, true, msg.correlationId, msg.sessionId))
//send the list of users in the meeting
outGW.send(new GetUsersReply(meetingID, msg.userId, users.getUsers, msg.sessionId))
//send chat history
this ! (new GetChatHistoryRequest(meetingID, msg.userId, msg.userId))
//join the user //join the user
handleUserJoin(new UserJoining(meetingID, msg.userId, msg.token)) handleUserJoin(new UserJoining(meetingID, msg.userId, msg.token))
//send the presentation //send the presentation
logger.info("ValidateToken success: mid=[" + meetingID + "] uid=[" + msg.userId + "]") logger.info("ValidateToken success: mid=[" + meetingID + "] uid=[" + msg.userId + "]")
this ! (new GetPresentationInfo(meetingID, msg.userId, msg.userId))
} }
case None => { case None => {
logger.info("ValidateToken failed: mid=[" + meetingID + "] uid=[" + msg.userId + "]") logger.info("ValidateToken failed: mid=[" + meetingID + "] uid=[" + msg.userId + "]")
@ -153,11 +146,14 @@ trait UsersApp {
case None => // do nothing case None => // do nothing
} }
} }
def handleGetLockSettings(msg: GetLockSettings) { def handleGetLockSettings(msg: GetLockSettings) {
logger.info("Not implemented: handleGetLockSettings") //println("*************** Reply with current lock settings ********************")
//reusing the existing handle for NewPermissionsSettings to reply to the GetLockSettings request
outGW.send(new NewPermissionsSetting(meetingID, msg.userId, permissions, users.getUsers))
} }
def handleSetLockSettings(msg: SetLockSettings) { def handleSetLockSettings(msg: SetLockSettings) {
// println("*************** Received new lock settings ********************") // println("*************** Received new lock settings ********************")
if (!permissionsEqual(msg.settings)) { if (!permissionsEqual(msg.settings)) {

View File

@ -160,7 +160,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mx:HBox width="100%" horizontalAlign="right" horizontalGap="18" paddingTop="20"> <mx:HBox width="100%" horizontalAlign="right" horizontalGap="18" paddingTop="20">
<mx:Button id="saveBtn" label="{ResourceUtil.getInstance().getString('bbb.lockSettings.save')}" <mx:Button id="saveBtn" label="{ResourceUtil.getInstance().getString('bbb.lockSettings.save')}"
click="onSaveClicked()" tabIndex="{baseIndex+8}" click="onSaveClicked()" tabIndex="{baseIndex+8}"
toolTip="{ResourceUtil.getInstance().getString('bbb.lockSettings.save.toolTip')}"/> toolTip="{ResourceUtil.getInstance().getString('bbb.lockSettings.save.tooltip')}"/>
<mx:Button id="cancelBtn" label="{ResourceUtil.getInstance().getString('bbb.lockSettings.cancel')}" <mx:Button id="cancelBtn" label="{ResourceUtil.getInstance().getString('bbb.lockSettings.cancel')}"
click="onCancelClicked()" tabIndex="{baseIndex+9}" click="onCancelClicked()" tabIndex="{baseIndex+9}"

View File

@ -87,6 +87,19 @@ https://github.com/bigbluebutton/bigbluebutton/blob/master/bigbluebutton-client/
BBB.isUserTalking = (userId, callback) -> BBB.isUserTalking = (userId, callback) ->
BBB.getUser(userId)?.user?.voiceUser?.talking BBB.getUser(userId)?.user?.voiceUser?.talking
# returns true if the current user is marked as locked
BBB.amILocked = () ->
return BBB.getCurrentUser()?.user.locked
# check whether the user is locked AND the current lock settings for the room
# includes locking the microphone of viewers (listenOnly is still alowed)
BBB.isMyMicLocked = () ->
lockedMicForRoom = Meteor.Meetings.findOne()?.roomLockSettings.disableMic
# note that voiceUser.locked is not used in BigBlueButton at this stage (April 2015)
return lockedMicForRoom and BBB.amILocked()
### ###
Raise user's hand. Raise user's hand.
@ -208,6 +221,8 @@ https://github.com/bigbluebutton/bigbluebutton/blob/master/bigbluebutton-client/
isListenOnly: signifies whether the user joining the conference audio requests to join the listen only stream isListenOnly: signifies whether the user joining the conference audio requests to join the listen only stream
### ###
BBB.joinVoiceConference = (callback, isListenOnly) -> BBB.joinVoiceConference = (callback, isListenOnly) ->
if BBB.isMyMicLocked()
callIntoConference(BBB.getMyVoiceBridge(), callback, true) #true because we force isListenOnly mode
callIntoConference(BBB.getMyVoiceBridge(), callback, isListenOnly) callIntoConference(BBB.getMyVoiceBridge(), callback, isListenOnly)
### ###

View File

@ -64,8 +64,6 @@ displayAudioSelectionMenu = ({isMobile} = {}) ->
if isMobile if isMobile
toggleSlidingMenu() toggleSlidingMenu()
if isMobile
$('.navbarTitle').css('width', '55%') $('.navbarTitle').css('width', '55%')
# pop open the dialog allowing users to choose the audio options # pop open the dialog allowing users to choose the audio options
@ -73,9 +71,20 @@ displayAudioSelectionMenu = ({isMobile} = {}) ->
$('.joinAudio-dialog').addClass('landscape-mobile-joinAudio-dialog') $('.joinAudio-dialog').addClass('landscape-mobile-joinAudio-dialog')
else else
$('.joinAudio-dialog').addClass('desktop-joinAudio-dialog') $('.joinAudio-dialog').addClass('desktop-joinAudio-dialog')
$("#joinAudioDialog").dialog("open") $("#joinAudioDialog").dialog("open")
# helper function to reuse some code for the handling of audio join
onAudioJoinHelper = () ->
# if the microphone is locked (lock settings), the viewer is only
# allowed to join the audio as listenOnly.
if BBB.isMyMicLocked()
introToAudio(null, isListenOnly: true)
else
displayAudioSelectionMenu(isMobile: isMobile())
# Helper to load javascript libraries from the BBB server # Helper to load javascript libraries from the BBB server
loadLib = (libname) -> loadLib = (libname) ->
successCallback = -> successCallback = ->
@ -113,7 +122,7 @@ Template.footer.helpers
Template.header.events Template.header.events
"click .joinAudioButton": (event) -> "click .joinAudioButton": (event) ->
displayAudioSelectionMenu(isMobile: false) onAudioJoinHelper()
"click .chatBarIcon": (event) -> "click .chatBarIcon": (event) ->
$(".tooltip").hide() $(".tooltip").hide()
@ -186,7 +195,7 @@ Template.header.events
Template.slidingMenu.events Template.slidingMenu.events
'click .joinAudioButton': (event) -> 'click .joinAudioButton': (event) ->
displayAudioSelectionMenu(isMobile: true) onAudioJoinHelper()
'click .chatBarIcon': (event) -> 'click .chatBarIcon': (event) ->
$('.tooltip').hide() $('.tooltip').hide()
@ -319,7 +328,7 @@ Template.main.rendered = ->
toggleSlidingMenu() toggleSlidingMenu()
if Meteor.config.app.autoJoinAudio if Meteor.config.app.autoJoinAudio
displayAudioSelectionMenu(isMobile:isMobile()) onAudioJoinHelper()
Template.makeButton.rendered = -> Template.makeButton.rendered = ->
$('button[rel=tooltip]').tooltip() $('button[rel=tooltip]').tooltip()

View File

@ -119,6 +119,12 @@
padding: 5px; padding: 5px;
} }
.disabledChat {
background-color: grey;
width: 100% !important;
}
.dropdown { .dropdown {
float: left; float: left;
@media @desktop-portrait, @mobile-portrait, @mobile-portrait-with-keyboard { @media @desktop-portrait, @mobile-portrait, @mobile-portrait-with-keyboard {

View File

@ -65,6 +65,24 @@ Handlebars.registerHelper "grabChatTabs", ->
setInSession 'chatTabs', initTabs setInSession 'chatTabs', initTabs
getInSession('chatTabs')[0..3] getInSession('chatTabs')[0..3]
# true if the lock settings limit public chat and the current user is locked
Handlebars.registerHelper "publicChatDisabled", ->
userIsLocked = Meteor.Users.findOne({userId:getInSession 'userId'})?.user.locked
publicChatIsDisabled = Meteor.Meetings.findOne({})?.roomLockSettings.disablePubChat
presenter = Meteor.Users.findOne({userId:getInSession 'userId'})?.user.presenter
return userIsLocked and publicChatIsDisabled and !presenter
# true if the lock settings limit private chat and the current user is locked
Handlebars.registerHelper "privateChatDisabled", ->
userIsLocked = Meteor.Users.findOne({userId:getInSession 'userId'})?.user.locked
privateChatIsDisabled = Meteor.Meetings.findOne({})?.roomLockSettings.disablePrivChat
presenter = Meteor.Users.findOne({userId:getInSession 'userId'})?.user.presenter
return userIsLocked and privateChatIsDisabled and !presenter
# return whether the user's chat pane is open in Private chat (vs Public chat or Options)
Handlebars.registerHelper "inPrivateChat", ->
return !((getInSession 'inChatWith') in ['PUBLIC_CHAT', 'OPTIONS'])
@sendMessage = -> @sendMessage = ->
message = linkify $('#newMessageInput').val() # get the message from the input box message = linkify $('#newMessageInput').val() # get the message from the input box
unless (message?.length > 0 and (/\S/.test(message))) # check the message has content and it is not whitespace unless (message?.length > 0 and (/\S/.test(message))) # check the message has content and it is not whitespace

View File

@ -2,7 +2,6 @@
<div id="{{id}}" {{visibility name}} class="component"> <div id="{{id}}" {{visibility name}} class="component">
<h3 class="title gradientBar"> <h3 class="title gradientBar">
<span class="glyphicon glyphicon-comment heading"></span> <span class="glyphicon glyphicon-comment heading"></span>
{{title}}
{{> extraConversations}} {{> extraConversations}}
</h3> </h3>
{{>tabButtons}} <!-- Display public/options tabs, and private chat tabs --> {{>tabButtons}} <!-- Display public/options tabs, and private chat tabs -->
@ -28,10 +27,25 @@
<template name="chatInput"> <template name="chatInput">
<div id="chatInput" class="chat-input-wrapper"> <div id="chatInput" class="chat-input-wrapper">
<textarea id="newMessageInput" placeholder="Write a message..." rel="tooltip" data-placement="top" title="Write a new message"></textarea> {{#if inPrivateChat}}
<button type="submit" id="sendMessageButton" class="btn" rel="tooltip" data-placement="top"> {{#if privateChatDisabled}}
Send <textarea id="newMessageInput" class="disabledChat" placeholder="Private chat is temporarily locked (disabled)" rel="tooltip" data-placement="top" title="Private chat is temporarily locked" disabled></textarea>
</button> {{else}}
<textarea id="newMessageInput" placeholder="Write a message..." rel="tooltip" data-placement="top" title="Write a new message"></textarea>
<button type="submit" id="sendMessageButton" class="btn" rel="tooltip" data-placement="top">
Send
</button>
{{/if}}
{{else}}
{{#if publicChatDisabled}}
<textarea id="newMessageInput" class="disabledChat" placeholder="Public chat is temporarily locked (disabled)" rel="tooltip" data-placement="top" title="Public chat is temporarily locked" disabled></textarea>
{{else}}
<textarea id="newMessageInput" placeholder="Write a message..." rel="tooltip" data-placement="top" title="Write a new message"></textarea>
<button type="submit" id="sendMessageButton" class="btn" rel="tooltip" data-placement="top">
Send
</button>
{{/if}}
{{/if}}
</div> </div>
</template> </template>

View File

@ -9,3 +9,16 @@ Template.displayUserIcons.events
# the userId of the person who is lowering the hand # the userId of the person who is lowering the hand
console.log "lower hand- client click handler" console.log "lower hand- client click handler"
Meteor.call('userLowerHand', getInSession("meetingId"), @userId, getInSession("userId"), getInSession("authToken")) Meteor.call('userLowerHand', getInSession("meetingId"), @userId, getInSession("userId"), getInSession("authToken"))
Template.displayUserIcons.helpers
userLockedIconApplicable: (userId) ->
# the lock settings affect the user (and requiire a lock icon) if
# the user is set to be locked and there is a relevant lock in place
locked = BBB.getUser(userId)?.user.locked
settings = Meteor.Meetings.findOne()?.roomLockSettings
lockInAction = settings.disablePrivChat or
settings.disableCam or
settings.disableMic or
settings.lockedLayout or
settings.disablePubChat
return locked and lockInAction

View File

@ -32,6 +32,10 @@
{{/if}} {{/if}}
{{/if}} {{/if}}
{{#if userLockedIconApplicable userId}}
<span class="userListSettingIcon glyphicon ion-locked" rel="tooltip" data-placement="bottom" title="The viewer is locked"></span>
{{/if}}
{{#if user.presenter}} {{#if user.presenter}}
<span class="userListSettingIcon glyphicon glyphicon-picture" rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter"></span> <span class="userListSettingIcon glyphicon glyphicon-picture" rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter"></span>
{{else}} {{else}}

View File

@ -14,6 +14,8 @@ config.defaultWelcomeMessageFooter = "This server is running a build of <a href=
config.maxUsernameLength = 30 config.maxUsernameLength = 30
config.maxChatLength = 140 config.maxChatLength = 140
config.lockOnJoin = true
## Application configurations ## Application configurations
config.app = {} config.app = {}

View File

@ -2,17 +2,27 @@
# Private methods on server # Private methods on server
# -------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------
@addMeetingToCollection = (meetingId, name, intendedForRecording, voiceConf, duration) -> @addMeetingToCollection = (meetingId, name, intendedForRecording, voiceConf, duration) ->
#check if the meeting is already in the collection #check if the meeting is already in the collection
unless Meteor.Meetings.findOne({meetingId: meetingId})? unless Meteor.Meetings.findOne({meetingId: meetingId})?
currentlyBeingRecorded = false # defaut value entry =
id = Meteor.Meetings.insert( meetingId: meetingId
meetingId: meetingId, meetingName: name
meetingName: name, intendedForRecording: intendedForRecording
intendedForRecording: intendedForRecording, currentlyBeingRecorded: false # defaut value
currentlyBeingRecorded: currentlyBeingRecorded, voiceConf: voiceConf
voiceConf: voiceConf, duration: duration
duration: duration) roomLockSettings:
Meteor.log.info "added meeting _id=[#{id}]:meetingId=[#{meetingId}]:name=[#{name}]:duration=[#{duration}]:voiceConf=[#{voiceConf}]." # by default the lock settings will be disabled on meeting create
disablePrivChat: false
disableCam: false
disableMic: false
lockOnJoin: Meteor.config.lockOnJoin
lockedLayout: false
disablePubChat: false
id = Meteor.Meetings.insert(entry)
Meteor.log.info "added meeting _id=[#{id}]:meetingId=[#{meetingId}]:name=[#{name}]:duration=[#{duration}]:voiceConf=[#{voiceConf}]
roomLockSettings:[#{JSON.stringify entry.roomLockSettings}]."
@clearMeetingsCollection = (meetingId) -> @clearMeetingsCollection = (meetingId) ->
@ -22,6 +32,7 @@
Meteor.Meetings.remove({}, Meteor.log.info "cleared Meetings Collection (all meetings)!") Meteor.Meetings.remove({}, Meteor.log.info "cleared Meetings Collection (all meetings)!")
#clean up upon a meeting's end
@removeMeetingFromCollection = (meetingId) -> @removeMeetingFromCollection = (meetingId) ->
if Meteor.Meetings.findOne({meetingId: meetingId})? if Meteor.Meetings.findOne({meetingId: meetingId})?
Meteor.log.info "end of meeting #{meetingId}. Clear the meeting data from all collections" Meteor.log.info "end of meeting #{meetingId}. Clear the meeting data from all collections"

View File

@ -204,7 +204,7 @@ Meteor.methods
# the collection already contains an entry for this user because # the collection already contains an entry for this user because
# we added a dummy user on register_user_message (to save authToken) # we added a dummy user on register_user_message (to save authToken)
if u? if u?
Meteor.log.info "UPDATING USER #{user.userid}, authToken=#{u.authToken}" Meteor.log.info "UPDATING USER #{user.userid}, authToken=#{u.authToken}, locked=#{user.locked}"
Meteor.Users.update({userId:user.userid, meetingId: meetingId}, {$set:{ Meteor.Users.update({userId:user.userid, meetingId: meetingId}, {$set:{
user: user:
userid: user.userid userid: user.userid
@ -308,6 +308,36 @@ Meteor.methods
Meteor.log.info "added user dummy html5 user with: userid=[#{userId}], id=[#{id}] Meteor.log.info "added user dummy html5 user with: userid=[#{userId}], id=[#{id}]
Users.size is now #{Meteor.Users.find({meetingId: meetingId}).count()}" Users.size is now #{Meteor.Users.find({meetingId: meetingId}).count()}"
# when new lock settings including disableMic are set,
# all viewers that are in the audio bridge with a mic should be muted and locked
@handleLockingMic = (meetingId, newSettings) ->
# send mute requests for the viewer users joined with mic
for u in Meteor.Users.find({
meetingId:meetingId
'user.role':'VIEWER'
'user.listenOnly':false
'user.locked':true
'user.voiceUser.joined':true
'user.voiceUser.muted':false})?.fetch()
Meteor.log.error u.user.name #
Meteor.call('muteUser', meetingId, u.userId, u.userId, u.authToken, true) #true for muted
# change the locked status of a user (lock settings)
@setUserLockedStatus = (meetingId, userId, isLocked) ->
if Meteor.Users.findOne({userId:userId, meetingId: meetingId})?
Meteor.Users.update({userId:userId, meetingId: meetingId}, {$set:{'user.locked': isLocked}})
# if the user is sharing audio, he should be muted upon locking involving disableMic
u = Meteor.Users.findOne({meetingId:meetingId, userId:userId})
if u.user.role is 'VIEWER' and !u.user.listenOnly and u.user.voiceUser.joined and !u.user.voiceUser.muted and isLocked
Meteor.call('muteUser', meetingId, u.userId, u.userId, u.authToken, true) #true for muted
Meteor.log.info "setting user locked status for userid:[#{userId}] from [#{meetingId}] locked=#{isLocked}"
else
Meteor.log.error "(unsuccessful-no such user) setting user locked status for userid:[#{userId}] from [#{meetingId}] locked=#{isLocked}"
# called on server start and on meeting end # called on server start and on meeting end
@clearUsersCollection = (meetingId) -> @clearUsersCollection = (meetingId) ->
if meetingId? if meetingId?

View File

@ -1,8 +1,4 @@
Meteor.methods Meteor.methods
#
# I dont know if this is okay to be server side. We need to call it from the router, but I don't know if any harm can be caused
# by the client calling this
#
# Construct and send a message to bbb-web to validate the user # Construct and send a message to bbb-web to validate the user
validateAuthToken: (meetingId, userId, authToken) -> validateAuthToken: (meetingId, userId, authToken) ->
@ -60,15 +56,25 @@ class Meteor.RedisPubSub
correlationId = message.payload?.reply_to or message.header?.reply_to correlationId = message.payload?.reply_to or message.header?.reply_to
meetingId = message.payload?.meeting_id meetingId = message.payload?.meeting_id
ignoredEventTypes = [ # just because it's common. we handle it anyway
notLoggedEventTypes = [
"keep_alive_reply" "keep_alive_reply"
"page_resized_message" "page_resized_message"
"presentation_page_resized_message" "presentation_page_resized_message"
"presentation_cursor_updated_message" # just because it's common. we handle it anyway "presentation_cursor_updated_message"
"get_presentation_info_reply"
# "get_users_reply"
"get_chat_history_reply"
"get_all_meetings_reply"
"presentation_shared_message"
"presentation_conversion_done_message"
"presentation_conversion_progress_message"
"presentation_page_generated_message"
"presentation_page_changed_message"
] ]
if message?.header? and message?.payload? if message?.header? and message?.payload?
unless message.header.name in ignoredEventTypes unless message.header.name in notLoggedEventTypes
Meteor.log.info "eventType= #{message.header.name} ", Meteor.log.info "eventType= #{message.header.name} ",
message: jsonMsg message: jsonMsg
@ -281,6 +287,37 @@ class Meteor.RedisPubSub
Meteor.Meetings.update({meetingId: meetingId, intendedForRecording: intendedForRecording}, {$set: {currentlyBeingRecorded: currentlyBeingRecorded}}) Meteor.Meetings.update({meetingId: meetingId, intendedForRecording: intendedForRecording}, {$set: {currentlyBeingRecorded: currentlyBeingRecorded}})
return return
# --------------------------------------------------
# lock settings ------------------------------------
if message.header.name is "eject_voice_user_message"
console.log "\n111111111"
return
if message.header.name is "new_permission_settings"
oldSettings = Meteor.Meetings.findOne({meetingId:meetingId})?.roomLockSettings
newSettings = message.payload
# if the disableMic setting was turned on
if !oldSettings?.disableMic and newSettings.disableMic
handleLockingMic(meetingId, newSettings)
# substitute with the new lock settings
Meteor.Meetings.update({meetingId: meetingId}, {$set: {
'roomLockSettings.disablePrivChat': message.payload.disablePrivChat
'roomLockSettings.disableCam': message.payload.disableCam
'roomLockSettings.disableMic': message.payload.disableMic
'roomLockSettings.lockOnJoin': message.payload.lockOnJoin
'roomLockSettings.lockedLayout': message.payload.lockedLayout
'roomLockSettings.disablePubChat': message.payload.disablePubChat
}})
return
if message.header.name is "user_locked_message" or message.header.name is "user_unlocked_message"
userId = message.payload.userid
isLocked = message.payload.locked
setUserLockedStatus(meetingId, userId, isLocked)
return
if message.header.name in ["meeting_ended_message", "meeting_destroyed_event", if message.header.name in ["meeting_ended_message", "meeting_destroyed_event",
"end_and_kick_all_message", "disconnect_all_users_message"] "end_and_kick_all_message", "disconnect_all_users_message"]
if Meteor.Meetings.findOne({meetingId: meetingId})? if Meteor.Meetings.findOne({meetingId: meetingId})?
@ -298,9 +335,9 @@ class Meteor.RedisPubSub
# message should be an object # message should be an object
@publish = (channel, message) -> @publish = (channel, message) ->
Meteor.log.info "Publishing", # Meteor.log.info "Publishing",
channel: channel # channel: channel
message: message # message: message
if Meteor.redisPubSub? if Meteor.redisPubSub?
Meteor.redisPubSub.pubClient.publish channel, JSON.stringify(message), (err, res) -> Meteor.redisPubSub.pubClient.publish channel, JSON.stringify(message), (err, res) ->

View File

@ -1,8 +1,16 @@
presenter = null presenter = null
#for the time being moderators have the same permissions that viewers do # holds the values for whether the moderator user is allowed to perform an action (true)
# or false if not allowed. Some actions have dynamic values depending on the current lock settings
moderator = moderator =
# audio listen only
joinListenOnly: true
leaveListenOnly: true
# join audio with mic cannot be controlled on the server side as it is
# a client side only functionality
# raising/lowering hand # raising/lowering hand
raiseOwnHand : true raiseOwnHand : true
lowerOwnHand : true lowerOwnHand : true
@ -18,21 +26,29 @@ moderator =
subscribeChat: true subscribeChat: true
#chat #chat
chatPublic: true #should make this dynamically modifiable later on chatPublic: true
chatPrivate: true #should make this dynamically modifiable later on chatPrivate: true
# holds the values for whether the viewer user is allowed to perform an action (true)
# or false if not allowed. Some actions have dynamic values depending on the current lock settings
viewer = (meetingId, userId) ->
# listen only # listen only
joinListenOnly: true #should make this dynamically modifiable later on joinListenOnly: true
leaveListenOnly: true #should make this dynamically modifiable later on leaveListenOnly: true
# join audio with mic cannot be controlled on the server side as it is
# a client side only functionality
viewer =
# raising/lowering hand # raising/lowering hand
raiseOwnHand : true raiseOwnHand : true
lowerOwnHand : true lowerOwnHand : true
# muting # muting
muteSelf : true muteSelf : true
unmuteSelf : true unmuteSelf : !(Meteor.Meetings.findOne({meetingId:meetingId})?.roomLockSettings.disableMic) or
!(Meteor.Users.findOne({meetingId:meetingId, userId:userId})?.user.locked)
logoutSelf : true logoutSelf : true
@ -41,15 +57,18 @@ viewer =
subscribeChat: true subscribeChat: true
#chat #chat
chatPublic: true #should make this dynamically modifiable later on chatPublic: !(Meteor.Meetings.findOne({meetingId:meetingId})?.roomLockSettings.disablePubChat) or
chatPrivate: true #should make this dynamically modifiable later on !(Meteor.Users.findOne({meetingId:meetingId, userId:userId})?.user.locked) or
Meteor.Users.findOne({meetingId:meetingId, userId:userId})?.user.presenter
chatPrivate: !(Meteor.Meetings.findOne({meetingId:meetingId})?.roomLockSettings.disablePrivChat) or
!(Meteor.Users.findOne({meetingId:meetingId, userId:userId})?.user.locked) or
Meteor.Users.findOne({meetingId:meetingId, userId:userId})?.user.presenter
# listen only
joinListenOnly: true #should make this dynamically modifiable later on
leaveListenOnly: true #should make this dynamically modifiable later on
# carries out the decision making for actions affecting users. For the list of
# actions and the default value - see 'viewer' and 'moderator' in the beginning of the file
@isAllowedTo = (action, meetingId, userId, authToken) -> @isAllowedTo = (action, meetingId, userId, authToken) ->
# Disclaimer:the current version of the HTML5 client represents only VIEWER users
validated = Meteor.Users.findOne({meetingId:meetingId, userId: userId})?.validated validated = Meteor.Users.findOne({meetingId:meetingId, userId: userId})?.validated
Meteor.log.info "in isAllowedTo: action-#{action}, userId=#{userId}, authToken=#{authToken} validated:#{validated}" Meteor.log.info "in isAllowedTo: action-#{action}, userId=#{userId}, authToken=#{authToken} validated:#{validated}"
@ -59,7 +78,7 @@ viewer =
if user? and authToken is user.authToken # check if the user is who he claims to be if user? and authToken is user.authToken # check if the user is who he claims to be
if user.validated and user.clientType is "HTML5" if user.validated and user.clientType is "HTML5"
if user.user?.role is 'VIEWER' or user.user?.role is 'MODERATOR' or user.user?.role is undefined if user.user?.role is 'VIEWER' or user.user?.role is 'MODERATOR' or user.user?.role is undefined
return viewer[action] or false return viewer(meetingId, userId)[action] or false
else else
Meteor.log.warn "UNSUCCESSFULL ATTEMPT FROM userid=#{userId} to perform:#{action}" Meteor.log.warn "UNSUCCESSFULL ATTEMPT FROM userid=#{userId} to perform:#{action}"
return false return false