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.
@@ -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
###
BBB.joinVoiceConference = (callback, isListenOnly) ->
+ if BBB.isMyMicLocked()
+ callIntoConference(BBB.getMyVoiceBridge(), callback, true) #true because we force isListenOnly mode
callIntoConference(BBB.getMyVoiceBridge(), callback, isListenOnly)
###
diff --git a/bigbluebutton-html5/app/client/main.coffee b/bigbluebutton-html5/app/client/main.coffee
index 16bbd3c1d0..0bb3207287 100755
--- a/bigbluebutton-html5/app/client/main.coffee
+++ b/bigbluebutton-html5/app/client/main.coffee
@@ -64,8 +64,6 @@ displayAudioSelectionMenu = ({isMobile} = {}) ->
if isMobile
toggleSlidingMenu()
-
- if isMobile
$('.navbarTitle').css('width', '55%')
# pop open the dialog allowing users to choose the audio options
@@ -73,9 +71,20 @@ displayAudioSelectionMenu = ({isMobile} = {}) ->
$('.joinAudio-dialog').addClass('landscape-mobile-joinAudio-dialog')
else
$('.joinAudio-dialog').addClass('desktop-joinAudio-dialog')
+
$("#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
loadLib = (libname) ->
successCallback = ->
@@ -113,7 +122,7 @@ Template.footer.helpers
Template.header.events
"click .joinAudioButton": (event) ->
- displayAudioSelectionMenu(isMobile: false)
+ onAudioJoinHelper()
"click .chatBarIcon": (event) ->
$(".tooltip").hide()
@@ -186,7 +195,7 @@ Template.header.events
Template.slidingMenu.events
'click .joinAudioButton': (event) ->
- displayAudioSelectionMenu(isMobile: true)
+ onAudioJoinHelper()
'click .chatBarIcon': (event) ->
$('.tooltip').hide()
@@ -319,7 +328,7 @@ Template.main.rendered = ->
toggleSlidingMenu()
if Meteor.config.app.autoJoinAudio
- displayAudioSelectionMenu(isMobile:isMobile())
+ onAudioJoinHelper()
Template.makeButton.rendered = ->
$('button[rel=tooltip]').tooltip()
diff --git a/bigbluebutton-html5/app/client/stylesheets/chat.less b/bigbluebutton-html5/app/client/stylesheets/chat.less
index f05ec5ad70..1407d1b95c 100755
--- a/bigbluebutton-html5/app/client/stylesheets/chat.less
+++ b/bigbluebutton-html5/app/client/stylesheets/chat.less
@@ -119,6 +119,12 @@
padding: 5px;
}
+
+.disabledChat {
+ background-color: grey;
+ width: 100% !important;
+}
+
.dropdown {
float: left;
@media @desktop-portrait, @mobile-portrait, @mobile-portrait-with-keyboard {
diff --git a/bigbluebutton-html5/app/client/views/chat/chat_bar.coffee b/bigbluebutton-html5/app/client/views/chat/chat_bar.coffee
index 625df4acf8..eb766d6510 100755
--- a/bigbluebutton-html5/app/client/views/chat/chat_bar.coffee
+++ b/bigbluebutton-html5/app/client/views/chat/chat_bar.coffee
@@ -65,6 +65,24 @@ Handlebars.registerHelper "grabChatTabs", ->
setInSession 'chatTabs', initTabs
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 = ->
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
diff --git a/bigbluebutton-html5/app/client/views/chat/chat_bar.html b/bigbluebutton-html5/app/client/views/chat/chat_bar.html
index b3e4526f3f..691352fb23 100755
--- a/bigbluebutton-html5/app/client/views/chat/chat_bar.html
+++ b/bigbluebutton-html5/app/client/views/chat/chat_bar.html
@@ -2,7 +2,6 @@
- {{title}}
{{> extraConversations}}
{{>tabButtons}}
@@ -28,10 +27,25 @@
-
-
+ {{#if inPrivateChat}}
+ {{#if privateChatDisabled}}
+
+ {{else}}
+
+
+ {{/if}}
+ {{else}}
+ {{#if publicChatDisabled}}
+
+ {{else}}
+
+
+ {{/if}}
+ {{/if}}
diff --git a/bigbluebutton-html5/app/client/views/users/user_item.coffee b/bigbluebutton-html5/app/client/views/users/user_item.coffee
index 24f33884fe..d9e7779ea6 100755
--- a/bigbluebutton-html5/app/client/views/users/user_item.coffee
+++ b/bigbluebutton-html5/app/client/views/users/user_item.coffee
@@ -9,3 +9,16 @@ Template.displayUserIcons.events
# the userId of the person who is lowering the hand
console.log "lower hand- client click handler"
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
diff --git a/bigbluebutton-html5/app/client/views/users/user_item.html b/bigbluebutton-html5/app/client/views/users/user_item.html
index 8b5ddd5bac..18df7410fd 100755
--- a/bigbluebutton-html5/app/client/views/users/user_item.html
+++ b/bigbluebutton-html5/app/client/views/users/user_item.html
@@ -32,6 +32,10 @@
{{/if}}
{{/if}}
+ {{#if userLockedIconApplicable userId}}
+
+ {{/if}}
+
{{#if user.presenter}}
{{else}}
diff --git a/bigbluebutton-html5/app/config.coffee b/bigbluebutton-html5/app/config.coffee
index 7e39027a54..cc606108e8 100755
--- a/bigbluebutton-html5/app/config.coffee
+++ b/bigbluebutton-html5/app/config.coffee
@@ -14,6 +14,8 @@ config.defaultWelcomeMessageFooter = "This server is running a build of
- #check if the meeting is already in the collection
- unless Meteor.Meetings.findOne({meetingId: meetingId})?
- currentlyBeingRecorded = false # defaut value
- id = Meteor.Meetings.insert(
- meetingId: meetingId,
- meetingName: name,
- intendedForRecording: intendedForRecording,
- currentlyBeingRecorded: currentlyBeingRecorded,
- voiceConf: voiceConf,
- duration: duration)
- Meteor.log.info "added meeting _id=[#{id}]:meetingId=[#{meetingId}]:name=[#{name}]:duration=[#{duration}]:voiceConf=[#{voiceConf}]."
+ #check if the meeting is already in the collection
+ unless Meteor.Meetings.findOne({meetingId: meetingId})?
+ entry =
+ meetingId: meetingId
+ meetingName: name
+ intendedForRecording: intendedForRecording
+ currentlyBeingRecorded: false # defaut value
+ voiceConf: voiceConf
+ duration: duration
+ roomLockSettings:
+ # 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) ->
@@ -22,6 +32,7 @@
Meteor.Meetings.remove({}, Meteor.log.info "cleared Meetings Collection (all meetings)!")
+#clean up upon a meeting's end
@removeMeetingFromCollection = (meetingId) ->
if Meteor.Meetings.findOne({meetingId: meetingId})?
Meteor.log.info "end of meeting #{meetingId}. Clear the meeting data from all collections"
diff --git a/bigbluebutton-html5/app/server/collection_methods/users.coffee b/bigbluebutton-html5/app/server/collection_methods/users.coffee
index 5e63688364..8b431dddb3 100755
--- a/bigbluebutton-html5/app/server/collection_methods/users.coffee
+++ b/bigbluebutton-html5/app/server/collection_methods/users.coffee
@@ -204,7 +204,7 @@ Meteor.methods
# the collection already contains an entry for this user because
# we added a dummy user on register_user_message (to save authToken)
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:{
user:
userid: user.userid
@@ -308,6 +308,36 @@ Meteor.methods
Meteor.log.info "added user dummy html5 user with: userid=[#{userId}], id=[#{id}]
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
@clearUsersCollection = (meetingId) ->
if meetingId?
diff --git a/bigbluebutton-html5/app/server/redispubsub.coffee b/bigbluebutton-html5/app/server/redispubsub.coffee
index 5ea8a05c08..3d81454bb1 100755
--- a/bigbluebutton-html5/app/server/redispubsub.coffee
+++ b/bigbluebutton-html5/app/server/redispubsub.coffee
@@ -1,8 +1,4 @@
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
validateAuthToken: (meetingId, userId, authToken) ->
@@ -60,15 +56,25 @@ class Meteor.RedisPubSub
correlationId = message.payload?.reply_to or message.header?.reply_to
meetingId = message.payload?.meeting_id
- ignoredEventTypes = [
+ # just because it's common. we handle it anyway
+ notLoggedEventTypes = [
"keep_alive_reply"
"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?
- unless message.header.name in ignoredEventTypes
+ unless message.header.name in notLoggedEventTypes
Meteor.log.info "eventType= #{message.header.name} ",
message: jsonMsg
@@ -281,6 +287,37 @@ class Meteor.RedisPubSub
Meteor.Meetings.update({meetingId: meetingId, intendedForRecording: intendedForRecording}, {$set: {currentlyBeingRecorded: currentlyBeingRecorded}})
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",
"end_and_kick_all_message", "disconnect_all_users_message"]
if Meteor.Meetings.findOne({meetingId: meetingId})?
@@ -298,9 +335,9 @@ class Meteor.RedisPubSub
# message should be an object
@publish = (channel, message) ->
- Meteor.log.info "Publishing",
- channel: channel
- message: message
+ # Meteor.log.info "Publishing",
+ # channel: channel
+ # message: message
if Meteor.redisPubSub?
Meteor.redisPubSub.pubClient.publish channel, JSON.stringify(message), (err, res) ->
diff --git a/bigbluebutton-html5/app/server/user_permissions.coffee b/bigbluebutton-html5/app/server/user_permissions.coffee
index b243e2dab0..ce3962ad81 100755
--- a/bigbluebutton-html5/app/server/user_permissions.coffee
+++ b/bigbluebutton-html5/app/server/user_permissions.coffee
@@ -1,8 +1,16 @@
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 =
+ # 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
raiseOwnHand : true
lowerOwnHand : true
@@ -18,21 +26,29 @@ moderator =
subscribeChat: true
#chat
- chatPublic: true #should make this dynamically modifiable later on
- chatPrivate: true #should make this dynamically modifiable later on
+ chatPublic: true
+ 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
- joinListenOnly: true #should make this dynamically modifiable later on
- leaveListenOnly: true #should make this dynamically modifiable later on
+ joinListenOnly: true
+ 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
raiseOwnHand : true
lowerOwnHand : true
# muting
muteSelf : true
- unmuteSelf : true
+ unmuteSelf : !(Meteor.Meetings.findOne({meetingId:meetingId})?.roomLockSettings.disableMic) or
+ !(Meteor.Users.findOne({meetingId:meetingId, userId:userId})?.user.locked)
logoutSelf : true
@@ -41,15 +57,18 @@ viewer =
subscribeChat: true
#chat
- chatPublic: true #should make this dynamically modifiable later on
- chatPrivate: true #should make this dynamically modifiable later on
+ chatPublic: !(Meteor.Meetings.findOne({meetingId:meetingId})?.roomLockSettings.disablePubChat) or
+ !(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) ->
- # Disclaimer:the current version of the HTML5 client represents only VIEWER users
validated = Meteor.Users.findOne({meetingId:meetingId, userId: userId})?.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.validated and user.clientType is "HTML5"
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
Meteor.log.warn "UNSUCCESSFULL ATTEMPT FROM userid=#{userId} to perform:#{action}"
return false