diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
index 9067f3b73c..78efc0bf0a 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
@@ -49,7 +49,6 @@ trait SystemConfiguration {
lazy val endMeetingWhenNoMoreAuthedUsers = Try(config.getBoolean("apps.endMeetingWhenNoMoreAuthedUsers")).getOrElse(false)
lazy val endMeetingWhenNoMoreAuthedUsersAfterMinutes = Try(config.getInt("apps.endMeetingWhenNoMoreAuthedUsersAfterMinutes")).getOrElse(2)
- lazy val multiUserWhiteboardDefault = Try(config.getBoolean("whiteboard.multiUserDefault")).getOrElse(false)
// Redis server configuration
lazy val redisHost = Try(config.getString("redis.host")).getOrElse("127.0.0.1")
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
index 8963b7057b..4bab7a2477 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
@@ -135,10 +135,6 @@ class BigBlueButtonActor(
RunningMeetings.add(meetings, m)
- // Send new 2x message
- val msgEvent = MsgBuilder.buildMeetingCreatedEvtMsg(m.props.meetingProp.intId, msg.body.props)
- m.outMsgRouter.send(msgEvent)
-
case Some(m) =>
log.info("Meeting already created. meetingID={}", msg.body.props.meetingProp.intId)
// do nothing
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/ScreenshareModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/ScreenshareModel.scala
index cae06abb02..dcf6934b67 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/ScreenshareModel.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/ScreenshareModel.scala
@@ -10,6 +10,7 @@ object ScreenshareModel {
status.voiceConf = ""
status.screenshareConf = ""
status.timestamp = ""
+ status.hasAudio = false
}
def getScreenshareStarted(status: ScreenshareModel): Boolean = {
@@ -79,6 +80,14 @@ object ScreenshareModel {
def getTimestamp(status: ScreenshareModel): String = {
status.timestamp
}
+
+ def setHasAudio(status: ScreenshareModel, hasAudio: Boolean): Unit = {
+ status.hasAudio = hasAudio
+ }
+
+ def getHasAudio(status: ScreenshareModel): Boolean = {
+ status.hasAudio
+ }
}
class ScreenshareModel {
@@ -90,4 +99,5 @@ class ScreenshareModel {
private var voiceConf: String = ""
private var screenshareConf: String = ""
private var timestamp: String = ""
+ private var hasAudio = false
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala
index a8a45901fb..486041bc1d 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala
@@ -25,7 +25,14 @@ class WhiteboardModel extends SystemConfiguration {
}
private def createWhiteboard(wbId: String): Whiteboard = {
- new Whiteboard(wbId, multiUserWhiteboardDefault, System.currentTimeMillis(), 0, new HashMap[String, List[AnnotationVO]]())
+ new Whiteboard(
+ wbId,
+ Array.empty[String],
+ Array.empty[String],
+ System.currentTimeMillis(),
+ 0,
+ new HashMap[String, List[AnnotationVO]]()
+ )
}
private def getAnnotationsByUserId(wb: Whiteboard, id: String): List[AnnotationVO] = {
@@ -184,7 +191,7 @@ class WhiteboardModel extends SystemConfiguration {
if (hasWhiteboard(wbId)) {
val wb = getWhiteboard(wbId)
- if (wb.multiUser) {
+ if (wb.multiUser.contains(userId)) {
if (wb.annotationsMap.contains(userId)) {
val newWb = wb.copy(annotationsMap = wb.annotationsMap - userId)
saveWhiteboard(newWb)
@@ -205,7 +212,7 @@ class WhiteboardModel extends SystemConfiguration {
var last: Option[AnnotationVO] = None
val wb = getWhiteboard(wbId)
- if (wb.multiUser) {
+ if (wb.multiUser.contains(userId)) {
val usersAnnotations = getAnnotationsByUserId(wb, userId)
//not empty and head id equals annotation id
@@ -234,13 +241,21 @@ class WhiteboardModel extends SystemConfiguration {
wb.copy(annotationsMap = newAnnotationsMap)
}
- def modifyWhiteboardAccess(wbId: String, multiUser: Boolean) {
+ def modifyWhiteboardAccess(wbId: String, multiUser: Array[String]) {
val wb = getWhiteboard(wbId)
- val newWb = wb.copy(multiUser = multiUser, changedModeOn = System.currentTimeMillis())
+ val newWb = wb.copy(multiUser = multiUser, oldMultiUser = wb.multiUser, changedModeOn = System.currentTimeMillis())
saveWhiteboard(newWb)
}
- def getWhiteboardAccess(wbId: String): Boolean = getWhiteboard(wbId).multiUser
+ def getWhiteboardAccess(wbId: String): Array[String] = getWhiteboard(wbId).multiUser
+
+ def hasWhiteboardAccess(wbId: String, userId: String): Boolean = {
+ val wb = getWhiteboard(wbId)
+ wb.multiUser.contains(userId) || {
+ val lastChange = System.currentTimeMillis() - wb.changedModeOn
+ wb.oldMultiUser.contains(userId) && lastChange < 5000
+ }
+ }
def getChangedModeOn(wbId: String): Long = getWhiteboard(wbId).changedModeOn
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/GetScreenshareStatusReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/GetScreenshareStatusReqMsgHdlr.scala
index fbea876750..1edea1b84c 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/GetScreenshareStatusReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/GetScreenshareStatusReqMsgHdlr.scala
@@ -25,9 +25,10 @@ trait GetScreenshareStatusReqMsgHdlr {
val vidWidth = ScreenshareModel.getScreenshareVideoWidth(liveMeeting.screenshareModel)
val vidHeight = ScreenshareModel.getScreenshareVideoHeight(liveMeeting.screenshareModel)
val timestamp = ScreenshareModel.getTimestamp(liveMeeting.screenshareModel)
+ val hasAudio = ScreenshareModel.getHasAudio(liveMeeting.screenshareModel)
val body = ScreenshareRtmpBroadcastStartedEvtMsgBody(voiceConf, screenshareConf,
- stream, vidWidth, vidHeight, timestamp)
+ stream, vidWidth, vidHeight, timestamp, hasAudio)
val event = ScreenshareRtmpBroadcastStartedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgHdlr.scala
index e72788eb6b..60ee455a0f 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/screenshare/ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgHdlr.scala
@@ -10,7 +10,7 @@ trait ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgHdlr {
def handle(msg: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(voiceConf: String, screenshareConf: String, stream: String, vidWidth: Int, vidHeight: Int,
- timestamp: String): BbbCommonEnvCoreMsg = {
+ timestamp: String, hasAudio: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
@@ -23,7 +23,7 @@ trait ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgHdlr {
)
val body = ScreenshareRtmpBroadcastStartedEvtMsgBody(voiceConf, screenshareConf,
- stream, vidWidth, vidHeight, timestamp)
+ stream, vidWidth, vidHeight, timestamp, hasAudio)
val event = ScreenshareRtmpBroadcastStartedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
@@ -41,12 +41,13 @@ trait ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgHdlr {
ScreenshareModel.setVoiceConf(liveMeeting.screenshareModel, msg.body.voiceConf)
ScreenshareModel.setScreenshareConf(liveMeeting.screenshareModel, msg.body.screenshareConf)
ScreenshareModel.setTimestamp(liveMeeting.screenshareModel, msg.body.timestamp)
+ ScreenshareModel.setHasAudio(liveMeeting.screenshareModel, msg.body.hasAudio)
log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
// Notify viewers in the meeting that there's an rtmp stream to view
val msgEvent = broadcastEvent(msg.body.voiceConf, msg.body.screenshareConf, msg.body.stream,
- msg.body.vidWidth, msg.body.vidHeight, msg.body.timestamp)
+ msg.body.vidWidth, msg.body.vidHeight, msg.body.timestamp, msg.body.hasAudio)
bus.outGW.send(msgEvent)
} else {
log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ClearWhiteboardPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ClearWhiteboardPubMsgHdlr.scala
index b5d2d686a0..d1b210498a 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ClearWhiteboardPubMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ClearWhiteboardPubMsgHdlr.scala
@@ -21,7 +21,7 @@ trait ClearWhiteboardPubMsgHdlr extends RightsManagementTrait {
bus.outGW.send(msgEvent)
}
- if (filterWhiteboardMessage(msg.body.whiteboardId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
+ if (filterWhiteboardMessage(msg.body.whiteboardId, msg.header.userId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to clear the whiteboard."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/GetWhiteboardAnnotationsReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/GetWhiteboardAnnotationsReqMsgHdlr.scala
index c3f35364d3..a7cbfe1d64 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/GetWhiteboardAnnotationsReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/GetWhiteboardAnnotationsReqMsgHdlr.scala
@@ -9,7 +9,7 @@ trait GetWhiteboardAnnotationsReqMsgHdlr {
def handle(msg: GetWhiteboardAnnotationsReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
- def broadcastEvent(msg: GetWhiteboardAnnotationsReqMsg, history: Array[AnnotationVO], multiUser: Boolean): Unit = {
+ def broadcastEvent(msg: GetWhiteboardAnnotationsReqMsg, history: Array[AnnotationVO], multiUser: Array[String]): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val envelope = BbbCoreEnvelope(GetWhiteboardAnnotationsRespMsg.NAME, routing)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ModifyWhiteboardAccessPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ModifyWhiteboardAccessPubMsgHdlr.scala
index cd515b3197..f40382e5b9 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ModifyWhiteboardAccessPubMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/ModifyWhiteboardAccessPubMsgHdlr.scala
@@ -21,7 +21,7 @@ trait ModifyWhiteboardAccessPubMsgHdlr extends RightsManagementTrait {
bus.outGW.send(msgEvent)
}
- if (filterWhiteboardMessage(msg.body.whiteboardId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
+ if (filterWhiteboardMessage(msg.body.whiteboardId, msg.header.userId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to modify access to the whiteboard."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendCursorPositionPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendCursorPositionPubMsgHdlr.scala
index 0dee90a62f..98df4e9e99 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendCursorPositionPubMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendCursorPositionPubMsgHdlr.scala
@@ -21,7 +21,7 @@ trait SendCursorPositionPubMsgHdlr extends RightsManagementTrait {
bus.outGW.send(msgEvent)
}
- if (filterWhiteboardMessage(msg.body.whiteboardId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
+ if (filterWhiteboardMessage(msg.body.whiteboardId, msg.header.userId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to send your cursor position."
// Just drop messages as these might be delayed messages from multi-user whiteboard. Don't want to
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendWhiteboardAnnotationPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendWhiteboardAnnotationPubMsgHdlr.scala
index aa6e4ff65a..10dd993eac 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendWhiteboardAnnotationPubMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/SendWhiteboardAnnotationPubMsgHdlr.scala
@@ -71,7 +71,7 @@ trait SendWhiteboardAnnotationPubMsgHdlr extends RightsManagementTrait {
WhiteboardKeyUtil.DRAW_UPDATE_STATUS == annotation.status)
}
- if (!excludedWbMsg(msg.body.annotation) && filterWhiteboardMessage(msg.body.annotation.wbId, liveMeeting) && permissionFailed(
+ if (!excludedWbMsg(msg.body.annotation) && filterWhiteboardMessage(msg.body.annotation.wbId, msg.header.userId, liveMeeting) && permissionFailed(
PermissionCheck.GUEST_LEVEL,
PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId
)) {
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/UndoWhiteboardPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/UndoWhiteboardPubMsgHdlr.scala
index 03d0e663c8..8ccbf46f8c 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/UndoWhiteboardPubMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/UndoWhiteboardPubMsgHdlr.scala
@@ -21,7 +21,7 @@ trait UndoWhiteboardPubMsgHdlr extends RightsManagementTrait {
bus.outGW.send(msgEvent)
}
- if (filterWhiteboardMessage(msg.body.whiteboardId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
+ if (filterWhiteboardMessage(msg.body.whiteboardId, msg.header.userId, liveMeeting) && permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to undo an annotation."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/WhiteboardApp2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/WhiteboardApp2x.scala
index b6acea18a2..0f90ed1c16 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/WhiteboardApp2x.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/whiteboard/WhiteboardApp2x.scala
@@ -5,8 +5,16 @@ import akka.event.Logging
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.common2.msgs.AnnotationVO
import org.bigbluebutton.core.apps.WhiteboardKeyUtil
+import scala.collection.immutable.{ Map, List }
-case class Whiteboard(id: String, multiUser: Boolean, changedModeOn: Long, annotationCount: Int, annotationsMap: scala.collection.immutable.Map[String, scala.collection.immutable.List[AnnotationVO]])
+case class Whiteboard(
+ id: String,
+ multiUser: Array[String],
+ oldMultiUser: Array[String],
+ changedModeOn: Long,
+ annotationCount: Int,
+ annotationsMap: Map[String, List[AnnotationVO]]
+)
class WhiteboardApp2x(implicit val context: ActorContext)
extends SendCursorPositionPubMsgHdlr
@@ -56,18 +64,18 @@ class WhiteboardApp2x(implicit val context: ActorContext)
liveMeeting.wbModel.undoWhiteboard(whiteboardId, requesterId)
}
- def getWhiteboardAccess(whiteboardId: String, liveMeeting: LiveMeeting): Boolean = {
+ def getWhiteboardAccess(whiteboardId: String, liveMeeting: LiveMeeting): Array[String] = {
liveMeeting.wbModel.getWhiteboardAccess(whiteboardId)
}
- def modifyWhiteboardAccess(whiteboardId: String, multiUser: Boolean, liveMeeting: LiveMeeting) {
+ def modifyWhiteboardAccess(whiteboardId: String, multiUser: Array[String], liveMeeting: LiveMeeting) {
liveMeeting.wbModel.modifyWhiteboardAccess(whiteboardId, multiUser)
}
- def filterWhiteboardMessage(whiteboardId: String, liveMeeting: LiveMeeting): Boolean = {
+ def filterWhiteboardMessage(whiteboardId: String, userId: String, liveMeeting: LiveMeeting): Boolean = {
// Need to check if the wb mode change from multi-user to single-user. Give 5sec allowance to
// allow delayed messages to be handled as clients may have been sending messages while the wb
// mode was changed. (ralam nov 22, 2017)
- if (!liveMeeting.wbModel.getWhiteboardAccess(whiteboardId) && liveMeeting.wbModel.getChangedModeOn(whiteboardId) > 5000) true else false
+ !liveMeeting.wbModel.hasWhiteboardAccess(whiteboardId, userId)
}
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
index 066935415b..e72b3f4bf8 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
@@ -156,6 +156,10 @@ class MeetingActor(
var lastRttTestSentOn = System.currentTimeMillis()
+ // Send new 2x message
+ val msgEvent = MsgBuilder.buildMeetingCreatedEvtMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props)
+ outGW.send(msgEvent)
+
// Create a default public group chat
state = groupChatApp.handleCreateDefaultPublicGroupChat(state, liveMeeting, msgBus)
diff --git a/akka-bbb-apps/src/universal/conf/application.conf b/akka-bbb-apps/src/universal/conf/application.conf
index fd3963cf82..03186f423b 100755
--- a/akka-bbb-apps/src/universal/conf/application.conf
+++ b/akka-bbb-apps/src/universal/conf/application.conf
@@ -95,8 +95,3 @@ recording {
# set zero to disable chapter break
chapterBreakLengthInMinutes = 0
}
-
-whiteboard {
- multiUserDefault = false
-}
-
diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java
index f9327ec6ad..0242b524e0 100755
--- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java
+++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java
@@ -83,7 +83,7 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene
if (((ScreenshareRTMPBroadcastEvent) event).getBroadcast()) {
ScreenshareRTMPBroadcastEvent evt = (ScreenshareRTMPBroadcastEvent) event;
vcs.deskShareRTMPBroadcastStarted(evt.getRoom(), evt.getBroadcastingStreamUrl(),
- evt.getVideoWidth(), evt.getVideoHeight(), evt.getTimestamp());
+ evt.getVideoWidth(), evt.getVideoHeight(), evt.getTimestamp(), evt.getHasAudio());
} else {
ScreenshareRTMPBroadcastEvent evt = (ScreenshareRTMPBroadcastEvent) event;
vcs.deskShareRTMPBroadcastStopped(evt.getRoom(), evt.getBroadcastingStreamUrl(),
diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java
index 77012629d2..c60c269843 100755
--- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java
+++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java
@@ -55,7 +55,8 @@ public interface IVoiceConferenceService {
String streamname,
Integer videoWidth,
Integer videoHeight,
- String timestamp);
+ String timestamp,
+ boolean hasAudio);
void deskShareRTMPBroadcastStopped(String room,
String streamname,
diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/ScreenshareRTMPBroadcastEvent.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/ScreenshareRTMPBroadcastEvent.java
index ea8c826888..814a64ff1d 100755
--- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/ScreenshareRTMPBroadcastEvent.java
+++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/ScreenshareRTMPBroadcastEvent.java
@@ -25,6 +25,7 @@ public class ScreenshareRTMPBroadcastEvent extends VoiceConferenceEvent {
private String streamUrl;
private Integer vw;
private Integer vh;
+ private boolean hasAudio;
private final String SCREENSHARE_SUFFIX = "-SCREENSHARE";
@@ -46,6 +47,10 @@ public class ScreenshareRTMPBroadcastEvent extends VoiceConferenceEvent {
public void setVideoHeight(Integer vh) {this.vh = vh;}
+ public void setHasAudio(boolean hasAudio) {
+ this.hasAudio = hasAudio;
+ }
+
public Integer getVideoHeight() {return vh;}
public Integer getVideoWidth() {return vw;}
@@ -65,4 +70,8 @@ public class ScreenshareRTMPBroadcastEvent extends VoiceConferenceEvent {
public boolean getBroadcast() {
return broadcast;
}
+
+ public boolean getHasAudio() {
+ return hasAudio;
+ }
}
diff --git a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala
index 7d63c4f116..c6da363f8c 100755
--- a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala
+++ b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala
@@ -237,13 +237,14 @@ class VoiceConferenceService(healthz: HealthzService,
streamname: String,
vw: java.lang.Integer,
vh: java.lang.Integer,
- timestamp: String
+ timestamp: String,
+ hasAudio: Boolean
) {
val header = BbbCoreVoiceConfHeader(ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg.NAME, voiceConfId)
val body = ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgBody(voiceConf = voiceConfId, screenshareConf = voiceConfId,
stream = streamname, vidWidth = vw.intValue(), vidHeight = vh.intValue(),
- timestamp)
+ timestamp, hasAudio)
val envelope = BbbCoreEnvelope(ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg.NAME, Map("voiceConf" -> voiceConfId))
val msg = new ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(header, body)
diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala
index 9916ffddeb..a6d15a7ffa 100755
--- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala
+++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala
@@ -24,7 +24,7 @@ case class ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(
extends VoiceStandardMsg
case class ScreenshareRtmpBroadcastStartedVoiceConfEvtMsgBody(voiceConf: String, screenshareConf: String,
stream: String, vidWidth: Int, vidHeight: Int,
- timestamp: String)
+ timestamp: String, hasAudio: Boolean)
/**
* Sent to clients to notify them of an RTMP stream starting.
@@ -37,7 +37,7 @@ case class ScreenshareRtmpBroadcastStartedEvtMsg(
extends BbbCoreMsg
case class ScreenshareRtmpBroadcastStartedEvtMsgBody(voiceConf: String, screenshareConf: String,
stream: String, vidWidth: Int, vidHeight: Int,
- timestamp: String)
+ timestamp: String, hasAudio: Boolean)
/**
* Send by FS that RTMP stream has stopped.
diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/WhiteboardMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/WhiteboardMsgs.scala
index 707fa8e66f..7e79667bf3 100755
--- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/WhiteboardMsgs.scala
+++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/WhiteboardMsgs.scala
@@ -18,7 +18,7 @@ case class GetWhiteboardAnnotationsReqMsgBody(whiteboardId: String)
object ModifyWhiteboardAccessPubMsg { val NAME = "ModifyWhiteboardAccessPubMsg" }
case class ModifyWhiteboardAccessPubMsg(header: BbbClientMsgHeader, body: ModifyWhiteboardAccessPubMsgBody) extends StandardMsg
-case class ModifyWhiteboardAccessPubMsgBody(whiteboardId: String, multiUser: Boolean)
+case class ModifyWhiteboardAccessPubMsgBody(whiteboardId: String, multiUser: Array[String])
object SendCursorPositionPubMsg { val NAME = "SendCursorPositionPubMsg" }
case class SendCursorPositionPubMsg(header: BbbClientMsgHeader, body: SendCursorPositionPubMsgBody) extends StandardMsg
@@ -48,11 +48,11 @@ case class ClearWhiteboardEvtMsgBody(whiteboardId: String, userId: String, fullC
object GetWhiteboardAnnotationsRespMsg { val NAME = "GetWhiteboardAnnotationsRespMsg" }
case class GetWhiteboardAnnotationsRespMsg(header: BbbClientMsgHeader, body: GetWhiteboardAnnotationsRespMsgBody) extends BbbCoreMsg
-case class GetWhiteboardAnnotationsRespMsgBody(whiteboardId: String, annotations: Array[AnnotationVO], multiUser: Boolean)
+case class GetWhiteboardAnnotationsRespMsgBody(whiteboardId: String, annotations: Array[AnnotationVO], multiUser: Array[String])
object ModifyWhiteboardAccessEvtMsg { val NAME = "ModifyWhiteboardAccessEvtMsg" }
case class ModifyWhiteboardAccessEvtMsg(header: BbbClientMsgHeader, body: ModifyWhiteboardAccessEvtMsgBody) extends BbbCoreMsg
-case class ModifyWhiteboardAccessEvtMsgBody(whiteboardId: String, multiUser: Boolean)
+case class ModifyWhiteboardAccessEvtMsgBody(whiteboardId: String, multiUser: Array[String])
object SendCursorPositionEvtMsg { val NAME = "SendCursorPositionEvtMsg" }
case class SendCursorPositionEvtMsg(header: BbbClientMsgHeader, body: SendCursorPositionEvtMsgBody) extends BbbCoreMsg
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/ApiParams.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/ApiParams.java
index 94e835588a..40b81a2828 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/ApiParams.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/ApiParams.java
@@ -43,6 +43,7 @@ public class ApiParams {
public static final String MODERATOR_ONLY_MESSAGE = "moderatorOnlyMessage";
public static final String MODERATOR_PW = "moderatorPW";
public static final String MUTE_ON_START = "muteOnStart";
+ public static final String MEETING_KEEP_EVENTS = "meetingKeepEvents";
public static final String ALLOW_MODS_TO_UNMUTE_USERS = "allowModsToUnmuteUsers";
public static final String NAME = "name";
public static final String PARENT_MEETING_ID = "parentMeetingID";
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
index fad6f60344..bf495f2b8d 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
@@ -120,7 +120,6 @@ public class MeetingService implements MessageListener {
private RedisStorageService storeService;
private CallbackUrlService callbackUrlService;
private HTML5LoadBalancingService html5LoadBalancingService;
- private boolean keepEvents;
private long usersTimeout;
private long enteredUsersTimeout;
@@ -356,7 +355,7 @@ public class MeetingService implements MessageListener {
}
private boolean storeEvents(Meeting m) {
- return m.isRecord() || keepEvents;
+ return m.isRecord() || m.getMeetingKeepEvents();
}
private void handleCreateMeeting(Meeting m) {
@@ -404,6 +403,8 @@ public class MeetingService implements MessageListener {
logData.put("logCode", "create_meeting");
logData.put("description", "Create meeting.");
+ logData.put("meetingKeepEvents", m.getMeetingKeepEvents());
+
Gson gson = new Gson();
String logStr = gson.toJson(logData);
@@ -417,7 +418,7 @@ public class MeetingService implements MessageListener {
m.getDialNumber(), m.getMaxUsers(),
m.getMeetingExpireIfNoUserJoinedInMinutes(), m.getmeetingExpireWhenLastUserLeftInMinutes(),
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
- m.getUserActivitySignResponseDelayInMinutes(), m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), keepEvents,
+ m.getUserActivitySignResponseDelayInMinutes(), m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getMeetingKeepEvents(),
m.breakoutRoomsParams,
m.lockSettingsParams, m.getHtml5InstanceId());
}
@@ -697,7 +698,7 @@ public class MeetingService implements MessageListener {
if (m != null) {
m.setForciblyEnded(true);
processRecording(m);
- if (keepEvents) {
+ if (m.getMeetingKeepEvents()) {
// The creation of the ended tag must occur after the creation of the
// recorded tag to avoid concurrency issues at the recording scripts
recordingService.markAsEnded(m.getInternalId());
@@ -1233,10 +1234,6 @@ public class MeetingService implements MessageListener {
stunTurnService = s;
}
- public void setKeepEvents(boolean value) {
- keepEvents = value;
- }
-
public void setUsersTimeout(long value) {
usersTimeout = value;
}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
index 172265ded7..741d4099e0 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
@@ -87,6 +87,7 @@ public class ParamsProcessorUtil {
private boolean webcamsOnlyForModerator;
private boolean defaultMuteOnStart = false;
private boolean defaultAllowModsToUnmuteUsers = false;
+ private boolean defaultKeepEvents = false;
private boolean defaultBreakoutRoomsEnabled;
private boolean defaultBreakoutRoomsRecord;
@@ -544,6 +545,12 @@ public class ParamsProcessorUtil {
meeting.setMuteOnStart(muteOnStart);
+ Boolean meetingKeepEvents = defaultKeepEvents;
+ if (!StringUtils.isEmpty(params.get(ApiParams.MEETING_KEEP_EVENTS))) {
+ meetingKeepEvents = Boolean.parseBoolean(params.get(ApiParams.MEETING_KEEP_EVENTS));
+ }
+ meeting.setMeetingKeepEvents(meetingKeepEvents);
+
Boolean allowModsToUnmuteUsers = defaultAllowModsToUnmuteUsers;
if (!StringUtils.isEmpty(params.get(ApiParams.ALLOW_MODS_TO_UNMUTE_USERS))) {
allowModsToUnmuteUsers = Boolean.parseBoolean(params.get(ApiParams.ALLOW_MODS_TO_UNMUTE_USERS));
@@ -1026,6 +1033,10 @@ public class ParamsProcessorUtil {
return defaultMuteOnStart;
}
+ public void setDefaultKeepEvents(Boolean mke) {
+ defaultKeepEvents = mke;
+ }
+
public void setAllowModsToUnmuteUsers(Boolean value) {
defaultAllowModsToUnmuteUsers = value;
}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
index e2884db344..b17b07c7fb 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
@@ -84,6 +84,7 @@ public class Meeting {
private String customCopyright = "";
private Boolean muteOnStart = false;
private Boolean allowModsToUnmuteUsers = false;
+ private Boolean meetingKeepEvents;
private Integer meetingExpireIfNoUserJoinedInMinutes = 5;
private Integer meetingExpireWhenLastUserLeftInMinutes = 1;
@@ -503,6 +504,14 @@ public class Meeting {
return muteOnStart;
}
+ public void setMeetingKeepEvents(Boolean mke) {
+ meetingKeepEvents = mke;
+ }
+
+ public Boolean getMeetingKeepEvents() {
+ return meetingKeepEvents;
+ }
+
public void setAllowModsToUnmuteUsers(Boolean value) {
allowModsToUnmuteUsers = value;
}
diff --git a/bigbluebutton-config/bigbluebutton-release b/bigbluebutton-config/bigbluebutton-release
index f4dbef9194..dbabfda03e 100644
--- a/bigbluebutton-config/bigbluebutton-release
+++ b/bigbluebutton-config/bigbluebutton-release
@@ -1,2 +1,2 @@
-BIGBLUEBUTTON_RELEASE=2.3.0-alpha8
+BIGBLUEBUTTON_RELEASE=2.3.0-beta-1
diff --git a/bigbluebutton-config/bin/apply-lib.sh b/bigbluebutton-config/bin/apply-lib.sh
index 605248b3a9..6cfb2cb427 100644
--- a/bigbluebutton-config/bin/apply-lib.sh
+++ b/bigbluebutton-config/bin/apply-lib.sh
@@ -23,15 +23,17 @@ else
SERVLET_DIR=/var/lib/tomcat7/webapps/bigbluebutton
fi
+BBB_WEB_ETC_CONFIG=/etc/bigbluebutton/bbb-web.properties
+
PROTOCOL=http
if [ -f $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties ]; then
- SERVER_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
- if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep bigbluebutton.web.serverURL | grep -q https; then
+ SERVER_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
+ if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $BBB_WEB_ETC_CONFIG | grep -v '#' | grep ^bigbluebutton.web.serverURL | tail -n 1 | grep -q https; then
PROTOCOL=https
fi
fi
-HOST=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
+HOST=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
HTML5_CONFIG=/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml
BBB_WEB_CONFIG=$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties
diff --git a/bigbluebutton-html5/client/main.html b/bigbluebutton-html5/client/main.html
index 00c5bc98cf..09a8a2af05 100755
--- a/bigbluebutton-html5/client/main.html
+++ b/bigbluebutton-html5/client/main.html
@@ -82,7 +82,6 @@ with BigBlueButton; if not, see .
-
diff --git a/bigbluebutton-html5/imports/api/annotations/server/handlers/whiteboardAnnotations.js b/bigbluebutton-html5/imports/api/annotations/server/handlers/whiteboardAnnotations.js
index 8aaf807894..2a1390002a 100755
--- a/bigbluebutton-html5/imports/api/annotations/server/handlers/whiteboardAnnotations.js
+++ b/bigbluebutton-html5/imports/api/annotations/server/handlers/whiteboardAnnotations.js
@@ -15,7 +15,7 @@ export default function handleWhiteboardAnnotations({ header, body }, meetingId)
check(annotations, Array);
check(whiteboardId, String);
- check(multiUser, Boolean);
+ check(multiUser, Array);
clearAnnotations(meetingId, whiteboardId);
diff --git a/bigbluebutton-html5/imports/api/annotations/server/methods/clearWhiteboard.js b/bigbluebutton-html5/imports/api/annotations/server/methods/clearWhiteboard.js
index 5c146c7c8e..2358f05014 100644
--- a/bigbluebutton-html5/imports/api/annotations/server/methods/clearWhiteboard.js
+++ b/bigbluebutton-html5/imports/api/annotations/server/methods/clearWhiteboard.js
@@ -10,6 +10,8 @@ export default function clearWhiteboard(whiteboardId) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(whiteboardId, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/annotations/server/methods/sendAnnotation.js b/bigbluebutton-html5/imports/api/annotations/server/methods/sendAnnotation.js
index daa01d8f24..4ba53872db 100755
--- a/bigbluebutton-html5/imports/api/annotations/server/methods/sendAnnotation.js
+++ b/bigbluebutton-html5/imports/api/annotations/server/methods/sendAnnotation.js
@@ -1,8 +1,12 @@
+import { check } from 'meteor/check';
import sendAnnotationHelper from './sendAnnotationHelper';
import { extractCredentials } from '/imports/api/common/server/helpers';
export default function sendAnnotation(annotation) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
sendAnnotationHelper(annotation, meetingId, requesterUserId);
}
diff --git a/bigbluebutton-html5/imports/api/annotations/server/methods/sendBulkAnnotations.js b/bigbluebutton-html5/imports/api/annotations/server/methods/sendBulkAnnotations.js
index fd4530e744..ba8b0c1428 100644
--- a/bigbluebutton-html5/imports/api/annotations/server/methods/sendBulkAnnotations.js
+++ b/bigbluebutton-html5/imports/api/annotations/server/methods/sendBulkAnnotations.js
@@ -1,8 +1,12 @@
import { extractCredentials } from '/imports/api/common/server/helpers';
import sendAnnotationHelper from './sendAnnotationHelper';
+import { check } from 'meteor/check';
export default function sendBulkAnnotations(payload) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
payload.forEach(annotation => sendAnnotationHelper(annotation, meetingId, requesterUserId));
}
diff --git a/bigbluebutton-html5/imports/api/annotations/server/methods/undoAnnotation.js b/bigbluebutton-html5/imports/api/annotations/server/methods/undoAnnotation.js
index bcab1c2b35..b3398e5061 100644
--- a/bigbluebutton-html5/imports/api/annotations/server/methods/undoAnnotation.js
+++ b/bigbluebutton-html5/imports/api/annotations/server/methods/undoAnnotation.js
@@ -10,6 +10,8 @@ export default function undoAnnotation(whiteboardId) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(whiteboardId, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js
index 84ede61437..b573f8d4b0 100755
--- a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js
+++ b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js
@@ -39,6 +39,7 @@ const WEBSOCKET_KEEP_ALIVE_DEBOUNCE = MEDIA.websocketKeepAliveDebounce || 10;
const TRACE_SIP = MEDIA.traceSip || false;
const AUDIO_MICROPHONE_CONSTRAINTS = Meteor.settings.public.app.defaultSettings
.application.microphoneConstraints;
+const SDP_SEMANTICS = MEDIA.sdpSemantics;
const getAudioSessionNumber = () => {
let currItem = parseInt(sessionStorage.getItem(AUDIO_SESSION_NUM_KEY), 10);
@@ -388,7 +389,7 @@ class SIPSession {
sessionDescriptionHandlerFactoryOptions: {
peerConnectionConfiguration: {
iceServers,
- sdpSemantics: 'plan-b',
+ sdpSemantics: SDP_SEMANTICS,
},
},
displayName: callerIdName,
diff --git a/bigbluebutton-html5/imports/api/breakouts/server/methods/createBreakout.js b/bigbluebutton-html5/imports/api/breakouts/server/methods/createBreakout.js
index 25b7af0db8..697be09077 100644
--- a/bigbluebutton-html5/imports/api/breakouts/server/methods/createBreakout.js
+++ b/bigbluebutton-html5/imports/api/breakouts/server/methods/createBreakout.js
@@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function createBreakoutRoom(rooms, durationInMinutes, record = false) {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -12,6 +13,9 @@ export default function createBreakoutRoom(rooms, durationInMinutes, record = fa
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const eventName = 'CreateBreakoutRoomsCmdMsg';
if (rooms.length > MAX_BREAKOUT_ROOMS) {
Logger.info(`Attempt to create breakout rooms with invalid number of rooms in meeting id=${meetingId}`);
diff --git a/bigbluebutton-html5/imports/api/breakouts/server/methods/requestJoinURL.js b/bigbluebutton-html5/imports/api/breakouts/server/methods/requestJoinURL.js
index ace15db4b8..71903a00ff 100755
--- a/bigbluebutton-html5/imports/api/breakouts/server/methods/requestJoinURL.js
+++ b/bigbluebutton-html5/imports/api/breakouts/server/methods/requestJoinURL.js
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function requestJoinURL({ breakoutId, userId: userIdToInvite }) {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -8,6 +9,9 @@ export default function requestJoinURL({ breakoutId, userId: userIdToInvite }) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const userId = userIdToInvite || requesterUserId;
const eventName = 'RequestBreakoutJoinURLReqMsg';
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js b/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js
index 29123dbd86..bf22f2eca2 100644
--- a/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js
+++ b/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js
@@ -11,12 +11,14 @@ export default function emitExternalVideoEvent(options) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const { status, playerStatus } = options;
- const user = Users.findOne({ meetingId, userId: requesterUserId })
+ const user = Users.findOne({ meetingId, userId: requesterUserId });
if (user && user.presenter) {
-
check(status, String);
check(playerStatus, {
rate: Match.Maybe(Number),
@@ -24,13 +26,14 @@ export default function emitExternalVideoEvent(options) {
state: Match.Maybe(Boolean),
});
- let rate = playerStatus.rate || 0;
- let time = playerStatus.time || 0;
- let state = playerStatus.state || 0;
- const payload = { status, rate, time, state };
+ const rate = playerStatus.rate || 0;
+ const time = playerStatus.time || 0;
+ const state = playerStatus.state || 0;
+ const payload = {
+ status, rate, time, state,
+ };
Logger.debug(`User id=${requesterUserId} sending ${EVENT_NAME} event:${state} for meeting ${meetingId}`);
return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
-
}
}
diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/chatMessageBeforeJoinCounter.js b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/chatMessageBeforeJoinCounter.js
index f1e9d1141f..175eee0cdb 100644
--- a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/chatMessageBeforeJoinCounter.js
+++ b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/chatMessageBeforeJoinCounter.js
@@ -10,6 +10,10 @@ const PUBLIC_CHAT_TYPE = CHAT_CONFIG.type_public;
export default function chatMessageBeforeJoinCounter() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const groupChats = GroupChat.find({
$or: [
{ meetingId, access: PUBLIC_CHAT_TYPE },
diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/clearPublicChatHistory.js b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/clearPublicChatHistory.js
index df88282d41..f0d9259234 100644
--- a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/clearPublicChatHistory.js
+++ b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/clearPublicChatHistory.js
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function clearPublicChatHistory() {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -11,6 +12,9 @@ export default function clearPublicChatHistory() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const payload = {
chatId: PUBLIC_GROUP_CHAT_ID,
};
diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/fetchMessagePerPage.js b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/fetchMessagePerPage.js
index 8a00d3d593..32549e3d9e 100644
--- a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/fetchMessagePerPage.js
+++ b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/fetchMessagePerPage.js
@@ -1,14 +1,18 @@
import { Meteor } from 'meteor/meteor';
-import GroupChat from '/imports/api/group-chat';
import { GroupChatMsg } from '/imports/api/group-chat-msg';
import Users from '/imports/api/users';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
const CHAT_CONFIG = Meteor.settings.public.chat;
const ITENS_PER_PAGE = CHAT_CONFIG.itemsPerPage;
export default function fetchMessagePerPage(chatId, page) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const User = Users.findOne({ userId: requesterUserId, meetingId });
const messages = GroupChatMsg.find({ chatId, meetingId, timestamp: { $lt: User.authTokenValidatedTime } },
diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/sendGroupChatMsg.js b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/sendGroupChatMsg.js
index f486cdfe6c..d83853d207 100644
--- a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/sendGroupChatMsg.js
+++ b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/sendGroupChatMsg.js
@@ -34,6 +34,8 @@ export default function sendGroupChatMsg(chatId, message) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(message, Object);
const parsedMessage = parseMessage(message.message);
diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/startUserTyping.js b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/startUserTyping.js
index b46288d80e..f58126a9af 100644
--- a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/startUserTyping.js
+++ b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/startUserTyping.js
@@ -11,6 +11,8 @@ export default function startUserTyping(chatId) {
const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(chatId, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/stopUserTyping.js b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/stopUserTyping.js
index 619ad4963a..35bd4c12fe 100644
--- a/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/stopUserTyping.js
+++ b/bigbluebutton-html5/imports/api/group-chat-msg/server/methods/stopUserTyping.js
@@ -1,10 +1,14 @@
import { UsersTyping } from '/imports/api/group-chat-msg';
import stopTyping from '../modifiers/stopTyping';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function stopUserTyping() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const userTyping = UsersTyping.findOne({
meetingId,
userId: requesterUserId,
diff --git a/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js b/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js
index 2244289cc8..11d8904e4e 100644
--- a/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js
+++ b/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js
@@ -11,6 +11,8 @@ export default function createGroupChat(receiver) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(receiver, Object);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/group-chat/server/methods/destroyGroupChat.js b/bigbluebutton-html5/imports/api/group-chat/server/methods/destroyGroupChat.js
index bc0b859f95..5c418c1f7a 100644
--- a/bigbluebutton-html5/imports/api/group-chat/server/methods/destroyGroupChat.js
+++ b/bigbluebutton-html5/imports/api/group-chat/server/methods/destroyGroupChat.js
@@ -1,12 +1,16 @@
import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function destroyGroupChat() {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const eventName = 'DestroyGroupChatReqMsg';
const payload = {
diff --git a/bigbluebutton-html5/imports/api/guest-users/server/methods/allowPendingUsers.js b/bigbluebutton-html5/imports/api/guest-users/server/methods/allowPendingUsers.js
index 55837d56c2..d65a11a95e 100644
--- a/bigbluebutton-html5/imports/api/guest-users/server/methods/allowPendingUsers.js
+++ b/bigbluebutton-html5/imports/api/guest-users/server/methods/allowPendingUsers.js
@@ -11,6 +11,8 @@ const EVENT_NAME = 'GuestsWaitingApprovedMsg';
export default function allowPendingUsers(guests, status) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(guests, Array);
const mappedGuests = guests.map(guest => ({ status, guest: guest.intId }));
diff --git a/bigbluebutton-html5/imports/api/guest-users/server/methods/changeGuestPolicy.js b/bigbluebutton-html5/imports/api/guest-users/server/methods/changeGuestPolicy.js
index d0f7e140e5..873b1a6eae 100644
--- a/bigbluebutton-html5/imports/api/guest-users/server/methods/changeGuestPolicy.js
+++ b/bigbluebutton-html5/imports/api/guest-users/server/methods/changeGuestPolicy.js
@@ -11,6 +11,8 @@ const EVENT_NAME = 'SetGuestPolicyCmdMsg';
export default function changeGuestPolicy(policyRule) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(policyRule, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/local-settings/server/methods/userChangedLocalSettings.js b/bigbluebutton-html5/imports/api/local-settings/server/methods/userChangedLocalSettings.js
index 4cb6a5d561..ab611118f0 100644
--- a/bigbluebutton-html5/imports/api/local-settings/server/methods/userChangedLocalSettings.js
+++ b/bigbluebutton-html5/imports/api/local-settings/server/methods/userChangedLocalSettings.js
@@ -10,6 +10,8 @@ export default function userChangedLocalSettings(settings) {
if (!meetingId || !requesterUserId) return;
check(settings, Object);
+ check(meetingId, String);
+ check(requesterUserId, String);
const userLocalSettings = LocalSettings
.findOne({ meetingId, userId: requesterUserId },
diff --git a/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js b/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js
index e13e7c5f2d..041b4ae23b 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js
@@ -4,6 +4,7 @@ import handleGetAllMeetings from './handlers/getAllMeetings';
import handleMeetingEnd from './handlers/meetingEnd';
import handleMeetingDestruction from './handlers/meetingDestruction';
import handleMeetingLocksChange from './handlers/meetingLockChange';
+import handleGuestPolicyChanged from './handlers/guestPolicyChanged';
import handleGuestLobbyMessageChanged from './handlers/guestLobbyMessageChanged';
import handleUserLockChange from './handlers/userLockChange';
import handleRecordingStatusChange from './handlers/recordingStatusChange';
@@ -22,6 +23,7 @@ RedisPubSub.on('RecordingStatusChangedEvtMsg', handleRecordingStatusChange);
RedisPubSub.on('UpdateRecordingTimerEvtMsg', handleRecordingTimerChange);
RedisPubSub.on('WebcamsOnlyForModeratorChangedEvtMsg', handleChangeWebcamOnlyModerator);
RedisPubSub.on('GetLockSettingsRespMsg', handleMeetingLocksChange);
+RedisPubSub.on('GuestPolicyChangedEvtMsg', handleGuestPolicyChanged);
RedisPubSub.on('GuestLobbyMessageChangedEvtMsg', handleGuestLobbyMessageChanged);
RedisPubSub.on('MeetingTimeRemainingUpdateEvtMsg', handleTimeRemainingUpdate);
RedisPubSub.on('SelectRandomViewerRespMsg', handleSelectRandomViewer);
diff --git a/bigbluebutton-html5/imports/api/meetings/server/handlers/guestPolicyChanged.js b/bigbluebutton-html5/imports/api/meetings/server/handlers/guestPolicyChanged.js
new file mode 100644
index 0000000000..b7e42aeb9e
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/meetings/server/handlers/guestPolicyChanged.js
@@ -0,0 +1,12 @@
+import setGuestPolicy from '../modifiers/setGuestPolicy';
+import { check } from 'meteor/check';
+
+export default function handleGuestPolicyChanged({ body }, meetingId) {
+ const { policy } = body;
+
+ check(meetingId, String);
+ check(policy, String);
+
+
+ return setGuestPolicy(meetingId, policy);
+}
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/clearRandomlySelectedUser.js b/bigbluebutton-html5/imports/api/meetings/server/methods/clearRandomlySelectedUser.js
index 1cd5303db8..c7d46f4d75 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/methods/clearRandomlySelectedUser.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/clearRandomlySelectedUser.js
@@ -1,10 +1,14 @@
import Logger from '/imports/startup/server/logger';
import Meetings from '/imports/api/meetings';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function clearRandomlySelectedUser() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const selector = {
meetingId,
};
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/endMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/methods/endMeeting.js
index b77bc4ce31..d73c09c670 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/methods/endMeeting.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/endMeeting.js
@@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function endMeeting() {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -9,6 +10,9 @@ export default function endMeeting() {
const EVENT_NAME = 'LogoutAndEndMeetingCmdMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const payload = {
userId: requesterUserId,
};
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js
index 152d603947..cbb945774f 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js
@@ -9,6 +9,8 @@ export default function toggleLockSettings(lockSettingsProps) {
const EVENT_NAME = 'ChangeLockSettingsInMeetingCmdMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(lockSettingsProps, {
disableCam: Boolean,
disableMic: Boolean,
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleRecording.js b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleRecording.js
index 0bf20e5211..0ba3a5d034 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleRecording.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleRecording.js
@@ -4,6 +4,7 @@ import RedisPubSub from '/imports/startup/server/redis';
import { RecordMeetings } from '/imports/api/meetings';
import Users from '/imports/api/users';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function toggleRecording() {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -11,6 +12,10 @@ export default function toggleRecording() {
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const EVENT_NAME = 'SetRecordingStatusCmdMsg';
let meetingRecorded;
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js
index 93553bb128..303b9b4972 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js
@@ -9,6 +9,9 @@ export default function toggleWebcamsOnlyForModerator(webcamsOnlyForModerator) {
const EVENT_NAME = 'UpdateWebcamsOnlyForModeratorCmdMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
check(webcamsOnlyForModerator, Boolean);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/transferUser.js b/bigbluebutton-html5/imports/api/meetings/server/methods/transferUser.js
index 1bc56d624f..deca48c848 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/methods/transferUser.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/transferUser.js
@@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
-
+import { check } from 'meteor/check';
export default function transferUser(fromMeetingId, toMeetingId) {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -11,6 +11,9 @@ export default function transferUser(fromMeetingId, toMeetingId) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const payload = {
fromMeetingId,
toMeetingId,
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/setGuestPolicy.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/setGuestPolicy.js
new file mode 100644
index 0000000000..bc6ae4e1bb
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/setGuestPolicy.js
@@ -0,0 +1,28 @@
+import Meetings from '/imports/api/meetings';
+import Logger from '/imports/startup/server/logger';
+import { check } from 'meteor/check';
+
+export default function setGuestPolicy(meetingId, guestPolicy) {
+ check(meetingId, String);
+ check(guestPolicy, String);
+
+ const selector = {
+ meetingId,
+ };
+
+ const modifier = {
+ $set: {
+ 'usersProp.guestPolicy': guestPolicy,
+ },
+ };
+
+ try {
+ const { numberAffected } = Meetings.upsert(selector, modifier);
+
+ if (numberAffected) {
+ Logger.verbose(`Set guest policy meetingId=${meetingId} guestPolicy=${guestPolicy}`);
+ }
+ } catch (err) {
+ Logger.error(`Setting guest policy: ${err}`);
+ }
+}
diff --git a/bigbluebutton-html5/imports/api/network-information/server/methods/userInstabilityDetected.js b/bigbluebutton-html5/imports/api/network-information/server/methods/userInstabilityDetected.js
index 07db47ba63..bb4ecc43ae 100644
--- a/bigbluebutton-html5/imports/api/network-information/server/methods/userInstabilityDetected.js
+++ b/bigbluebutton-html5/imports/api/network-information/server/methods/userInstabilityDetected.js
@@ -5,6 +5,9 @@ import { extractCredentials } from '/imports/api/common/server/helpers';
export default function userInstabilityDetected(sender) {
const { meetingId, requesterUserId: receiver } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(receiver, String);
check(sender, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/sendPollChatMsg.js b/bigbluebutton-html5/imports/api/polls/server/handlers/sendPollChatMsg.js
index 686dc31ec5..8870fdb41d 100644
--- a/bigbluebutton-html5/imports/api/polls/server/handlers/sendPollChatMsg.js
+++ b/bigbluebutton-html5/imports/api/polls/server/handlers/sendPollChatMsg.js
@@ -11,12 +11,28 @@ export default function sendPollChatMsg({ body }, meetingId) {
const { answers, numRespondents } = poll;
+ const caseInsensitiveReducer = (acc, item) => {
+ const index = acc.findIndex(ans => ans.key.toLowerCase() === item.key.toLowerCase());
+ if(index !== -1) {
+ if(acc[index].numVotes >= item.numVotes) acc[index].numVotes += item.numVotes;
+ else {
+ const tempVotes = acc[index].numVotes;
+ acc[index] = item;
+ acc[index].numVotes += tempVotes;
+ }
+ } else {
+ acc.push(item);
+ }
+ return acc;
+ };
+
let responded = 0;
let resultString = 'bbb-published-poll-\n';
answers.map((item) => {
responded += item.numVotes;
return item;
- }).map((item) => {
+ }).reduce(caseInsensitiveReducer, []).map((item) => {
+ item.key = item.key.split('
').join('
');
const numResponded = responded === numRespondents ? numRespondents : responded;
const pct = Math.round(item.numVotes / numResponded * 100);
const pctFotmatted = `${Number.isNaN(pct) ? 0 : pct}%`;
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/publishPoll.js b/bigbluebutton-html5/imports/api/polls/server/methods/publishPoll.js
index 701bea3b5c..738c043712 100644
--- a/bigbluebutton-html5/imports/api/polls/server/methods/publishPoll.js
+++ b/bigbluebutton-html5/imports/api/polls/server/methods/publishPoll.js
@@ -2,6 +2,7 @@ import RedisPubSub from '/imports/startup/server/redis';
import Polls from '/imports/api/polls';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function publishPoll() {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -9,6 +10,10 @@ export default function publishPoll() {
const EVENT_NAME = 'ShowPollResultReqMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const poll = Polls.findOne({ meetingId }); // TODO--send pollid from client
if (!poll) {
Logger.error(`Attempted to publish inexisting poll for meetingId: ${meetingId}`);
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/publishTypedVote.js b/bigbluebutton-html5/imports/api/polls/server/methods/publishTypedVote.js
index ffd29cc537..2e7ec5ee79 100644
--- a/bigbluebutton-html5/imports/api/polls/server/methods/publishTypedVote.js
+++ b/bigbluebutton-html5/imports/api/polls/server/methods/publishTypedVote.js
@@ -10,6 +10,8 @@ export default function publishTypedVote(id, pollAnswer) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(pollAnswer, String);
check(id, String);
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js b/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js
index 6aabfe10c6..a09be1043f 100644
--- a/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js
+++ b/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js
@@ -10,6 +10,8 @@ export default function publishVote(pollId, pollAnswerId) {
const EVENT_NAME = 'RespondToPollReqMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(pollAnswerId, Number);
check(pollId, String);
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js b/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js
index 2fe305dd48..b3b67ff5f5 100644
--- a/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js
+++ b/bigbluebutton-html5/imports/api/polls/server/methods/startPoll.js
@@ -10,6 +10,8 @@ export default function startPoll(pollType, pollId, question, answers) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(pollId, String);
check(pollType, String);
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/stopPoll.js b/bigbluebutton-html5/imports/api/polls/server/methods/stopPoll.js
index af0c50dd00..7a40397638 100644
--- a/bigbluebutton-html5/imports/api/polls/server/methods/stopPoll.js
+++ b/bigbluebutton-html5/imports/api/polls/server/methods/stopPoll.js
@@ -1,8 +1,13 @@
import RedisPubSub from '/imports/startup/server/redis';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function stopPoll() {
const { meetingId, requesterUserId: requesterId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterId, String);
+
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'StopPollReqMsg';
diff --git a/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js b/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js
index b01937dea0..f7331c15b7 100644
--- a/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js
+++ b/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js
@@ -8,6 +8,9 @@ export default function requestPresentationUploadToken(podId, filename) {
const EVENT_NAME = 'PresentationUploadTokenReqMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
check(podId, String);
check(filename, String);
diff --git a/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/setUsedToken.js b/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/setUsedToken.js
index 6efd96aa48..413aeaafa7 100644
--- a/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/setUsedToken.js
+++ b/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/setUsedToken.js
@@ -1,10 +1,14 @@
import PresentationUploadToken from '/imports/api/presentation-upload-token';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function setUsedToken(authzToken) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const payload = {
$set: {
used: true,
diff --git a/bigbluebutton-html5/imports/api/presentations/server/methods/removePresentation.js b/bigbluebutton-html5/imports/api/presentations/server/methods/removePresentation.js
index a4169aa0f3..1bf2c92345 100644
--- a/bigbluebutton-html5/imports/api/presentations/server/methods/removePresentation.js
+++ b/bigbluebutton-html5/imports/api/presentations/server/methods/removePresentation.js
@@ -9,6 +9,8 @@ export default function removePresentation(presentationId, podId) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(presentationId, String);
check(podId, String);
diff --git a/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentation.js b/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentation.js
index ebe15cbe0d..8d277b7f41 100644
--- a/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentation.js
+++ b/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentation.js
@@ -8,6 +8,8 @@ export default function setPresentation(presentationId, podId) {
const EVENT_NAME = 'SetCurrentPresentationPubMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(presentationId, String);
check(podId, String);
diff --git a/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentationDownloadable.js b/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentationDownloadable.js
index 5d51af14f0..d8604fde26 100644
--- a/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentationDownloadable.js
+++ b/bigbluebutton-html5/imports/api/presentations/server/methods/setPresentationDownloadable.js
@@ -9,6 +9,8 @@ export default function setPresentationDownloadable(presentationId, downloadable
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(downloadable, Boolean);
check(presentationId, String);
diff --git a/bigbluebutton-html5/imports/api/screenshare/client/bridge/errors.js b/bigbluebutton-html5/imports/api/screenshare/client/bridge/errors.js
new file mode 100644
index 0000000000..0adbe86643
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/screenshare/client/bridge/errors.js
@@ -0,0 +1,61 @@
+import {
+ SFU_CLIENT_SIDE_ERRORS,
+ SFU_SERVER_SIDE_ERRORS
+} from '/imports/ui/services/bbb-webrtc-sfu/broker-base-errors';
+
+// Mapped getDisplayMedia errors. These are bridge agnostic
+// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia
+const GDM_ERRORS = {
+ // Fallback error: 1130
+ 1130: 'GetDisplayMediaGenericError',
+ 1131: 'AbortError',
+ 1132: 'InvalidStateError',
+ 1133: 'OverconstrainedError',
+ 1134: 'TypeError',
+ 1135: 'NotFoundError',
+ 1136: 'NotAllowedError',
+ 1137: 'NotSupportedError',
+ 1138: 'NotReadableError',
+};
+
+// Import as many bridge specific errors you want in this utilitary and shove
+// them into the error class slots down below.
+const CLIENT_SIDE_ERRORS = {
+ 1101: "SIGNALLING_TRANSPORT_DISCONNECTED",
+ 1102: "SIGNALLING_TRANSPORT_CONNECTION_FAILED",
+ 1104: "SCREENSHARE_PLAY_FAILED",
+ 1105: "PEER_NEGOTIATION_FAILED",
+ 1107: "ICE_STATE_FAILED",
+ 1120: "MEDIA_TIMEOUT",
+ 1121: "UNKNOWN_ERROR",
+};
+
+const SERVER_SIDE_ERRORS = {
+ ...SFU_SERVER_SIDE_ERRORS,
+}
+
+const AGGREGATED_ERRORS = {
+ ...CLIENT_SIDE_ERRORS,
+ ...SERVER_SIDE_ERRORS,
+ ...GDM_ERRORS,
+}
+
+const expandErrors = () => {
+ const expandedErrors = Object.keys(AGGREGATED_ERRORS).reduce((map, key) => {
+ map[AGGREGATED_ERRORS[key]] = { errorCode: key, errorMessage: AGGREGATED_ERRORS[key] };
+ return map;
+ }, {});
+
+ return { ...AGGREGATED_ERRORS, ...expandedErrors };
+}
+
+const SCREENSHARING_ERRORS = expandErrors();
+
+export {
+ GDM_ERRORS,
+ BRIDGE_SERVER_SIDE_ERRORS,
+ BRIDGE_CLIENT_SIDE_ERRORS,
+ // All errors, [code]: [message]
+ // Expanded errors. It's AGGREGATED + message: { errorCode, errorMessage }
+ SCREENSHARING_ERRORS,
+}
diff --git a/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js b/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js
index d3ffdff3b8..d16adb4c2c 100755
--- a/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js
+++ b/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js
@@ -1,236 +1,297 @@
import Auth from '/imports/ui/services/auth';
-import BridgeService from './service';
-import { fetchWebRTCMappedStunTurnServers, getMappedFallbackStun } from '/imports/utils/fetchStunTurnServers';
-import playAndRetry from '/imports/utils/mediaElementPlayRetry';
import logger from '/imports/startup/client/logger';
+import BridgeService from './service';
+import ScreenshareBroker from '/imports/ui/services/bbb-webrtc-sfu/screenshare-broker';
+import { setSharingScreen, screenShareEndAlert } from '/imports/ui/components/screenshare/service';
+import { SCREENSHARING_ERRORS } from './errors';
const SFU_CONFIG = Meteor.settings.public.kurento;
const SFU_URL = SFU_CONFIG.wsUrl;
-const CHROME_DEFAULT_EXTENSION_KEY = SFU_CONFIG.chromeDefaultExtensionKey;
-const CHROME_CUSTOM_EXTENSION_KEY = SFU_CONFIG.chromeExtensionKey;
-const CHROME_SCREENSHARE_SOURCES = SFU_CONFIG.screenshare.chromeScreenshareSources;
-const FIREFOX_SCREENSHARE_SOURCE = SFU_CONFIG.screenshare.firefoxScreenshareSource;
+
+const BRIDGE_NAME = 'kurento'
const SCREENSHARE_VIDEO_TAG = 'screenshareVideo';
+const SEND_ROLE = 'send';
+const RECV_ROLE = 'recv';
-const CHROME_EXTENSION_KEY = CHROME_CUSTOM_EXTENSION_KEY === 'KEY' ? CHROME_DEFAULT_EXTENSION_KEY : CHROME_CUSTOM_EXTENSION_KEY;
+// the error-code mapping is bridge specific; that's why it's not in the errors util
+const ERROR_MAP = {
+ 1301: SCREENSHARING_ERRORS.SIGNALLING_TRANSPORT_DISCONNECTED,
+ 1302: SCREENSHARING_ERRORS.SIGNALLING_TRANSPORT_CONNECTION_FAILED,
+ 1305: SCREENSHARING_ERRORS.PEER_NEGOTIATION_FAILED,
+ 1307: SCREENSHARING_ERRORS.ICE_STATE_FAILED,
+}
-const getUserId = () => Auth.userID;
+const mapErrorCode = (error) => {
+ const { errorCode } = error;
+ const mappedError = ERROR_MAP[errorCode];
-const getMeetingId = () => Auth.meetingID;
+ if (errorCode == null || mappedError == null) return error;
+ error.errorCode = mappedError.errorCode;
+ error.errorMessage = mappedError.errorMessage;
+ error.message = mappedError.errorMessage;
-const getUsername = () => Auth.fullname;
-
-const getSessionToken = () => Auth.sessionToken;
+ return error;
+}
export default class KurentoScreenshareBridge {
- static normalizeError(error = {}) {
- const errorMessage = error.name || error.message || error.reason || 'Unknown error';
- const errorCode = error.code || undefined;
- const errorReason = error.reason || error.id || 'Undefined reason';
-
- return { errorMessage, errorCode, errorReason };
+ constructor() {
+ this.role;
+ this.broker;
+ this._gdmStream;
+ this.hasAudio = false;
+ this.connectionAttempts = 0;
+ this.reconnecting = false;
+ this.reconnectionTimeout;
+ this.restartIntervalMs = BridgeService.BASE_MEDIA_TIMEOUT;
}
- static handlePresenterFailure(error, started = false) {
- const normalizedError = KurentoScreenshareBridge.normalizeError(error);
- if (!started) {
- logger.error({
- logCode: 'screenshare_presenter_error_failed_to_connect',
- extraInfo: { ...normalizedError },
- }, `Screenshare presenter failed when trying to start due to ${normalizedError.errorMessage}`);
- } else {
- logger.error({
- logCode: 'screenshare_presenter_error_failed_after_success',
- extraInfo: { ...normalizedError },
- }, `Screenshare presenter failed during working session due to ${normalizedError.errorMessage}`);
+ get gdmStream() {
+ return this._gdmStream;
+ }
+
+ set gdmStream(stream) {
+ this._gdmStream = stream;
+ }
+
+ outboundStreamReconnect() {
+ const currentRestartIntervalMs = this.restartIntervalMs;
+ const stream = this.gdmStream;
+
+ logger.warn({
+ logCode: 'screenshare_presenter_reconnect',
+ extraInfo: {
+ reconnecting: this.reconnecting,
+ role: this.role,
+ bridge: BRIDGE_NAME
+ },
+ }, `Screenshare presenter session is reconnecting`);
+
+ this.stop();
+ this.restartIntervalMs = BridgeService.getNextReconnectionInterval(currentRestartIntervalMs);
+ this.share(stream, this.onerror).then(() => {
+ this.clearReconnectionTimeout();
+ }).catch(error => {
+ // Error handling is a no-op because it will be "handled" in handlePresenterFailure
+ logger.debug({
+ logCode: 'screenshare_reconnect_failed',
+ extraInfo: {
+ errorCode: error.errorCode,
+ errorMessage: error.errorMessage,
+ reconnecting: this.reconnecting,
+ role: this.role,
+ bridge: BRIDGE_NAME
+ },
+ }, 'Screensharing reconnect failed');
+ });
+ }
+
+ inboundStreamReconnect() {
+ const currentRestartIntervalMs = this.restartIntervalMs;
+
+ logger.warn({
+ logCode: 'screenshare_viewer_reconnect',
+ extraInfo: {
+ reconnecting: this.reconnecting,
+ role: this.role,
+ bridge: BRIDGE_NAME
+ },
+ }, `Screenshare viewer session is reconnecting`);
+
+ // Cleanly stop everything before triggering a reconnect
+ this.stop();
+ // Create new reconnect interval time
+ this.restartIntervalMs = BridgeService.getNextReconnectionInterval(currentRestartIntervalMs);
+ this.view(this.hasAudio).then(() => {
+ this.clearReconnectionTimeout();
+ }).catch(error => {
+ // Error handling is a no-op because it will be "handled" in handleViewerFailure
+ logger.debug({
+ logCode: 'screenshare_reconnect_failed',
+ extraInfo: {
+ errorCode: error.errorCode,
+ errorMessage: error.errorMessage,
+ reconnecting: this.reconnecting,
+ role: this.role,
+ bridge: BRIDGE_NAME
+ },
+ }, 'Screensharing reconnect failed');
+ });
+ }
+
+ handleConnectionTimeoutExpiry() {
+ this.reconnecting = true;
+
+ switch (this.role) {
+ case RECV_ROLE:
+ return this.inboundStreamReconnect();
+ case SEND_ROLE:
+ return this.outboundStreamReconnect();
+ default:
+ this.reconnecting = false;
+ logger.error({
+ logCode: 'screenshare_invalid_role'
+ }, 'Screen sharing with invalid role, wont reconnect');
+ break;
}
- return normalizedError;
}
- static handleViewerFailure(error, started = false) {
- const normalizedError = KurentoScreenshareBridge.normalizeError(error);
- if (!started) {
- logger.error({
- logCode: 'screenshare_viewer_error_failed_to_connect',
- extraInfo: { ...normalizedError },
- }, `Screenshare viewer failed when trying to start due to ${normalizedError.errorMessage}`);
- } else {
- logger.error({
- logCode: 'screenshare_viewer_error_failed_after_success',
- extraInfo: { ...normalizedError },
- }, `Screenshare viewer failed during working session due to ${normalizedError.errorMessage}`);
+ maxConnectionAttemptsReached () {
+ return this.connectionAttempts > BridgeService.MAX_CONN_ATTEMPTS;
+ }
+
+ scheduleReconnect () {
+ if (this.reconnectionTimeout == null) {
+ this.reconnectionTimeout = setTimeout(
+ this.handleConnectionTimeoutExpiry.bind(this),
+ this.restartIntervalMs
+ );
}
- return normalizedError;
}
- static playElement(screenshareMediaElement) {
- const mediaTagPlayed = () => {
- logger.info({
- logCode: 'screenshare_media_play_success',
- }, 'Screenshare media played successfully');
+ clearReconnectionTimeout () {
+ this.reconnecting = false;
+ this.restartIntervalMs = BridgeService.BASE_MEDIA_TIMEOUT;
+
+ if (this.reconnectionTimeout) {
+ clearTimeout(this.reconnectionTimeout);
+ this.reconnectionTimeout = null;
+ }
+ }
+
+ handleViewerStart() {
+ const mediaElement = document.getElementById(SCREENSHARE_VIDEO_TAG);
+
+ if (mediaElement && this.broker && this.broker.webRtcPeer) {
+ const stream = this.broker.webRtcPeer.getRemoteStream();
+ BridgeService.screenshareLoadAndPlayMediaStream(stream, mediaElement, !this.broker.hasAudio);
+ }
+
+ this.clearReconnectionTimeout();
+ }
+
+ handleBrokerFailure(error) {
+ mapErrorCode(error);
+ const { errorMessage, errorCode } = error;
+
+ logger.error({
+ logCode: 'screenshare_broker_failure',
+ extraInfo: {
+ errorCode, errorMessage,
+ role: this.broker.role,
+ started: this.broker.started,
+ reconnecting: this.reconnecting,
+ bridge: BRIDGE_NAME
+ },
+ }, 'Screenshare broker failure');
+
+ // Screensharing was already successfully negotiated and error occurred during
+ // during call; schedule a reconnect
+ // If the session has not yet started, a reconnect should already be scheduled
+ if (this.broker.started) {
+ this.scheduleReconnect();
+ }
+
+ return error;
+ }
+
+ async view(hasAudio = false) {
+ this.hasAudio = hasAudio;
+ this.role = RECV_ROLE;
+ const iceServers = await BridgeService.getIceServers(Auth.sessionToken);
+ const options = {
+ iceServers,
+ userName: Auth.fullname,
+ hasAudio,
};
- if (screenshareMediaElement.paused) {
- // Tag isn't playing yet. Play it.
- screenshareMediaElement.play()
- .then(mediaTagPlayed)
- .catch((error) => {
- // NotAllowedError equals autoplay issues, fire autoplay handling event.
- // This will be handled in the screenshare react component.
- if (error.name === 'NotAllowedError') {
- logger.error({
- logCode: 'screenshare_error_autoplay',
- extraInfo: { errorName: error.name },
- }, 'Screenshare play failed due to autoplay error');
- const tagFailedEvent = new CustomEvent('screensharePlayFailed',
- { detail: { mediaElement: screenshareMediaElement } });
- window.dispatchEvent(tagFailedEvent);
- } else {
- // Tag failed for reasons other than autoplay. Log the error and
- // try playing again a few times until it works or fails for good
- const played = playAndRetry(screenshareMediaElement);
- if (!played) {
- logger.error({
- logCode: 'screenshare_error_media_play_failed',
- extraInfo: { errorName: error.name },
- }, `Screenshare media play failed due to ${error.name}`);
- } else {
- mediaTagPlayed();
- }
- }
- });
- } else {
- // Media tag is already playing, so log a success. This is really a
- // logging fallback for a case that shouldn't happen. But if it does
- // (ie someone re-enables the autoPlay prop in the element), then it
- // means the stream is playing properly and it'll be logged.
- mediaTagPlayed();
- }
+ this.broker = new ScreenshareBroker(
+ Auth.authenticateURL(SFU_URL),
+ BridgeService.getConferenceBridge(),
+ Auth.userID,
+ Auth.meetingID,
+ this.role,
+ options,
+ );
+
+ this.broker.onstart = this.handleViewerStart.bind(this);
+ this.broker.onerror = this.handleBrokerFailure.bind(this);
+ this.broker.onended = this.handleEnded.bind(this);
+
+ return this.broker.view().finally(this.scheduleReconnect.bind(this));
}
- static screenshareElementLoadAndPlay(stream, element, muted) {
- element.muted = muted;
- element.pause();
- element.srcObject = stream;
- KurentoScreenshareBridge.playElement(element);
+ handlePresenterStart() {
+ logger.info({
+ logCode: 'screenshare_presenter_start_success',
+ }, 'Screenshare presenter started succesfully');
+ this.clearReconnectionTimeout();
+ this.reconnecting = false;
+ this.connectionAttempts = 0;
}
- kurentoViewLocalPreview() {
- const screenshareMediaElement = document.getElementById(SCREENSHARE_VIDEO_TAG);
- const { webRtcPeer } = window.kurentoManager.kurentoScreenshare;
-
- if (webRtcPeer) {
- const stream = webRtcPeer.getLocalStream();
- KurentoScreenshareBridge.screenshareElementLoadAndPlay(stream, screenshareMediaElement, true);
- }
+ handleEnded() {
+ screenShareEndAlert();
}
- async kurentoViewScreen() {
- const screenshareMediaElement = document.getElementById(SCREENSHARE_VIDEO_TAG);
- let iceServers = [];
- let started = false;
+ share(stream, onFailure) {
+ return new Promise(async (resolve, reject) => {
+ this.onerror = onFailure;
+ this.connectionAttempts += 1;
+ this.role = SEND_ROLE;
+ this.hasAudio = BridgeService.streamHasAudioTrack(stream);
+ this.gdmStream = stream;
- try {
- iceServers = await fetchWebRTCMappedStunTurnServers(getSessionToken());
- } catch (error) {
- logger.error({
- logCode: 'screenshare_viewer_fetchstunturninfo_error',
- extraInfo: { error },
- }, 'Screenshare bridge failed to fetch STUN/TURN info, using default');
- iceServers = getMappedFallbackStun();
- } finally {
- const options = {
- wsUrl: Auth.authenticateURL(SFU_URL),
- iceServers,
- logger,
- userName: getUsername(),
- };
+ const onerror = (error) => {
+ const normalizedError = this.handleBrokerFailure(error);
+ if (this.maxConnectionAttemptsReached()) {
+ this.clearReconnectionTimeout();
+ this.connectionAttempts = 0;
+ onFailure(SCREENSHARING_ERRORS.MEDIA_TIMEOUT);
- const onFail = (error) => {
- KurentoScreenshareBridge.handleViewerFailure(error, started);
- };
-
- // Callback for the kurento-extension.js script. It's called when the whole
- // negotiation with SFU is successful. This will load the stream into the
- // screenshare media element and play it manually.
- const onSuccess = () => {
- started = true;
- const { webRtcPeer } = window.kurentoManager.kurentoVideo;
- if (webRtcPeer) {
- const stream = webRtcPeer.getRemoteStream();
- KurentoScreenshareBridge.screenshareElementLoadAndPlay(
- stream,
- screenshareMediaElement,
- true,
- );
+ return reject(SCREENSHARING_ERRORS.MEDIA_TIMEOUT);
}
};
- window.kurentoWatchVideo(
- SCREENSHARE_VIDEO_TAG,
- BridgeService.getConferenceBridge(),
- getUserId(),
- getMeetingId(),
- onFail,
- onSuccess,
- options,
- );
- }
- }
-
- kurentoExitVideo() {
- window.kurentoExitVideo();
- }
-
- async kurentoShareScreen(onFail, stream) {
- let iceServers = [];
- try {
- iceServers = await fetchWebRTCMappedStunTurnServers(getSessionToken());
- } catch (error) {
- logger.error({ logCode: 'screenshare_presenter_fetchstunturninfo_error' },
-
- 'Screenshare bridge failed to fetch STUN/TURN info, using default');
- iceServers = getMappedFallbackStun();
- } finally {
+ const iceServers = await BridgeService.getIceServers(Auth.sessionToken);
const options = {
- wsUrl: Auth.authenticateURL(SFU_URL),
- chromeExtension: CHROME_EXTENSION_KEY,
- chromeScreenshareSources: CHROME_SCREENSHARE_SOURCES,
- firefoxScreenshareSource: FIREFOX_SCREENSHARE_SOURCE,
iceServers,
- logger,
- userName: getUsername(),
+ userName: Auth.fullname,
+ stream,
+ hasAudio: this.hasAudio,
};
- let started = false;
-
- const failureCallback = (error) => {
- const normalizedError = KurentoScreenshareBridge.handlePresenterFailure(error, started);
- onFail(normalizedError);
- };
-
- const successCallback = () => {
- started = true;
- logger.info({
- logCode: 'screenshare_presenter_start_success',
- }, 'Screenshare presenter started succesfully');
- };
-
- options.stream = stream || undefined;
-
- window.kurentoShareScreen(
- SCREENSHARE_VIDEO_TAG,
+ this.broker = new ScreenshareBroker(
+ Auth.authenticateURL(SFU_URL),
BridgeService.getConferenceBridge(),
- getUserId(),
- getMeetingId(),
- failureCallback,
- successCallback,
+ Auth.userID,
+ Auth.meetingID,
+ this.role,
options,
);
- }
- }
- kurentoExitScreenShare() {
- window.kurentoExitScreenShare();
+ this.broker.onerror = onerror.bind(this);
+ this.broker.onstreamended = this.stop.bind(this);
+ this.broker.onstart = this.handlePresenterStart.bind(this);
+ this.broker.onended = this.handleEnded.bind(this);
+
+ this.broker.share().then(() => {
+ this.scheduleReconnect();
+ return resolve();
+ }).catch(reject);
+ });
+ };
+
+ stop() {
+ if (this.broker) {
+ this.broker.stop();
+ // Checks if this session is a sharer and if it's not reconnecting
+ // If that's the case, clear the local sharing state in screen sharing UI
+ // component tracker to be extra sure we won't have any client-side state
+ // inconsistency - prlanzarin
+ if (this.broker.role === SEND_ROLE && !this.reconnecting) setSharingScreen(false);
+ this.broker = null;
+ }
+ this.gdmStream = null;
+ this.clearReconnectionTimeout();
}
}
diff --git a/bigbluebutton-html5/imports/api/screenshare/client/bridge/service.js b/bigbluebutton-html5/imports/api/screenshare/client/bridge/service.js
index a8c0c90175..202fc80a05 100644
--- a/bigbluebutton-html5/imports/api/screenshare/client/bridge/service.js
+++ b/bigbluebutton-html5/imports/api/screenshare/client/bridge/service.js
@@ -1,37 +1,66 @@
import Meetings from '/imports/api/meetings';
import logger from '/imports/startup/client/logger';
+import { fetchWebRTCMappedStunTurnServers, getMappedFallbackStun } from '/imports/utils/fetchStunTurnServers';
+import loadAndPlayMediaStream from '/imports/ui/services/bbb-webrtc-sfu/load-play';
+import { SCREENSHARING_ERRORS } from './errors';
const {
constraints: GDM_CONSTRAINTS,
+ mediaTimeouts: MEDIA_TIMEOUTS,
} = Meteor.settings.public.kurento.screenshare;
+const {
+ baseTimeout: BASE_MEDIA_TIMEOUT,
+ maxTimeout: MAX_MEDIA_TIMEOUT,
+ maxConnectionAttempts: MAX_CONN_ATTEMPTS,
+ timeoutIncreaseFactor: TIMEOUT_INCREASE_FACTOR,
+} = MEDIA_TIMEOUTS;
-const hasDisplayMedia = (typeof navigator.getDisplayMedia === 'function'
+const HAS_DISPLAY_MEDIA = (typeof navigator.getDisplayMedia === 'function'
|| (navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia === 'function'));
const getConferenceBridge = () => Meetings.findOne().voiceProp.voiceConf;
+const normalizeGetDisplayMediaError = (error) => {
+ return SCREENSHARING_ERRORS[error.name] || SCREENSHARING_ERRORS.GetDisplayMediaGenericError;
+};
+
+const getBoundGDM = () => {
+ if (typeof navigator.getDisplayMedia === 'function') {
+ return navigator.getDisplayMedia.bind(navigator);
+ } else if (navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia === 'function') {
+ return navigator.mediaDevices.getDisplayMedia.bind(navigator.mediaDevices);
+ }
+}
+
const getScreenStream = async () => {
const gDMCallback = (stream) => {
+ // Some older Chromium variants choke on gDM when audio: true by NOT generating
+ // a promise rejection AND not generating a valid input screen stream, need to
+ // work around that manually for now - prlanzarin
+ if (stream == null) {
+ return Promise.reject(SCREENSHARING_ERRORS.NotSupportedError);
+ }
+
if (typeof stream.getVideoTracks === 'function'
- && typeof constraints.video === 'object') {
- stream.getVideoTracks().forEach((track) => {
- if (typeof track.applyConstraints === 'function') {
- track.applyConstraints(constraints.video).catch((error) => {
+ && typeof GDM_CONSTRAINTS.video === 'object') {
+ stream.getVideoTracks().forEach(track => {
+ if (typeof track.applyConstraints === 'function') {
+ track.applyConstraints(GDM_CONSTRAINTS.video).catch(error => {
logger.warn({
logCode: 'screenshare_videoconstraint_failed',
extraInfo: { errorName: error.name, errorCode: error.code },
},
- 'Error applying screenshare video constraint');
+ 'Error applying screenshare video constraint');
});
}
});
}
if (typeof stream.getAudioTracks === 'function'
- && typeof constraints.audio === 'object') {
- stream.getAudioTracks().forEach((track) => {
- if (typeof track.applyConstraints === 'function') {
- track.applyConstraints(constraints.audio).catch((error) => {
+ && typeof GDM_CONSTRAINTS.audio === 'object') {
+ stream.getAudioTracks().forEach(track => {
+ if (typeof track.applyConstraints === 'function') {
+ track.applyConstraints(GDM_CONSTRAINTS.audio).catch(error => {
logger.warn({
logCode: 'screenshare_audioconstraint_failed',
extraInfo: { errorName: error.name, errorCode: error.code },
@@ -44,39 +73,81 @@ const getScreenStream = async () => {
return Promise.resolve(stream);
};
- const constraints = hasDisplayMedia ? GDM_CONSTRAINTS : null;
+ const getDisplayMedia = getBoundGDM();
- // getDisplayMedia isn't supported, generate no stream and let the legacy
- // constraint fetcher work its way on kurento-extension.js
- if (constraints == null) {
- return Promise.resolve();
- }
- if (typeof navigator.getDisplayMedia === 'function') {
- return navigator.getDisplayMedia(constraints)
+ if (typeof getDisplayMedia === 'function') {
+ return getDisplayMedia(GDM_CONSTRAINTS)
.then(gDMCallback)
- .catch((error) => {
+ .catch(error => {
+ const normalizedError = normalizeGetDisplayMediaError(error);
logger.error({
logCode: 'screenshare_getdisplaymedia_failed',
- extraInfo: { errorName: error.name, errorCode: error.code },
+ extraInfo: { errorCode: normalizedError.errorCode, errorMessage: normalizedError.errorMessage },
}, 'getDisplayMedia call failed');
- return Promise.resolve();
- });
- } if (navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia === 'function') {
- return navigator.mediaDevices.getDisplayMedia(constraints)
- .then(gDMCallback)
- .catch((error) => {
- logger.error({
- logCode: 'screenshare_getdisplaymedia_failed',
- extraInfo: { errorName: error.name, errorCode: error.code },
- }, 'getDisplayMedia call failed');
- return Promise.resolve();
+ return Promise.reject(normalizedError);
});
+ } else {
+ // getDisplayMedia isn't supported, error its way out
+ return Promise.reject(SCREENSHARING_ERRORS.NotSupportedError);
}
};
+const getIceServers = (sessionToken) => {
+ return fetchWebRTCMappedStunTurnServers(sessionToken).catch(error => {
+ logger.error({
+ logCode: 'screenshare_fetchstunturninfo_error',
+ extraInfo: { error }
+ }, 'Screenshare bridge failed to fetch STUN/TURN info');
+ return getMappedFallbackStun();
+ });
+}
+
+const getNextReconnectionInterval = (oldInterval) => {
+ return Math.min(
+ TIMEOUT_INCREASE_FACTOR * oldInterval,
+ MAX_MEDIA_TIMEOUT,
+ );
+}
+
+const streamHasAudioTrack = (stream) => {
+ return stream
+ && typeof stream.getAudioTracks === 'function'
+ && stream.getAudioTracks().length >= 1;
+}
+
+const dispatchAutoplayHandlingEvent = (mediaElement) => {
+ const tagFailedEvent = new CustomEvent('screensharePlayFailed',
+ { detail: { mediaElement } });
+ window.dispatchEvent(tagFailedEvent);
+}
+
+const screenshareLoadAndPlayMediaStream = (stream, mediaElement, muted) => {
+ return loadAndPlayMediaStream(stream, mediaElement, muted).catch(error => {
+ // NotAllowedError equals autoplay issues, fire autoplay handling event.
+ // This will be handled in the screenshare react component.
+ if (error.name === 'NotAllowedError') {
+ logger.error({
+ logCode: 'screenshare_error_autoplay',
+ extraInfo: { errorName: error.name },
+ }, 'Screen share media play failed: autoplay error');
+ dispatchAutoplayHandlingEvent(mediaElement);
+ } else {
+ throw {
+ errorCode: SCREENSHARING_ERRORS.SCREENSHARE_PLAY_FAILED.errorCode,
+ errorMessage: error.message || SCREENSHARING_ERRORS.SCREENSHARE_PLAY_FAILED.errorMessage,
+ };
+ }
+ });
+}
export default {
- hasDisplayMedia,
+ HAS_DISPLAY_MEDIA,
getConferenceBridge,
getScreenStream,
+ getIceServers,
+ getNextReconnectionInterval,
+ streamHasAudioTrack,
+ screenshareLoadAndPlayMediaStream,
+ BASE_MEDIA_TIMEOUT,
+ MAX_CONN_ATTEMPTS,
};
diff --git a/bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js b/bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js
index 2251095ced..3e57c7f264 100755
--- a/bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js
+++ b/bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js
@@ -11,6 +11,9 @@ export default function switchSlide(slideNumber, podId) { // TODO-- send present
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'SetCurrentPagePubMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
check(slideNumber, Number);
const selector = {
diff --git a/bigbluebutton-html5/imports/api/slides/server/methods/zoomSlide.js b/bigbluebutton-html5/imports/api/slides/server/methods/zoomSlide.js
index 3d247742f3..10b949fc5e 100755
--- a/bigbluebutton-html5/imports/api/slides/server/methods/zoomSlide.js
+++ b/bigbluebutton-html5/imports/api/slides/server/methods/zoomSlide.js
@@ -3,6 +3,7 @@ import { Slides } from '/imports/api/slides';
import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function zoomSlide(slideNumber, podId, widthRatio, heightRatio, x, y) { // TODO-- send presentationId and SlideId
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -11,6 +12,9 @@ export default function zoomSlide(slideNumber, podId, widthRatio, heightRatio, x
const EVENT_NAME = 'ResizeAndMovePagePubMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const selector = {
meetingId,
podId,
diff --git a/bigbluebutton-html5/imports/api/users-infos/server/methods/removeUserInformation.js b/bigbluebutton-html5/imports/api/users-infos/server/methods/removeUserInformation.js
index 4e35bbbb45..d297498521 100644
--- a/bigbluebutton-html5/imports/api/users-infos/server/methods/removeUserInformation.js
+++ b/bigbluebutton-html5/imports/api/users-infos/server/methods/removeUserInformation.js
@@ -1,9 +1,14 @@
import UserInfos from '/imports/api/users-infos';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function removeUserInformation() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const selector = {
meetingId,
requesterUserId,
diff --git a/bigbluebutton-html5/imports/api/users-infos/server/methods/requestUserInformation.js b/bigbluebutton-html5/imports/api/users-infos/server/methods/requestUserInformation.js
index ed89eedd8e..cff6199fed 100644
--- a/bigbluebutton-html5/imports/api/users-infos/server/methods/requestUserInformation.js
+++ b/bigbluebutton-html5/imports/api/users-infos/server/methods/requestUserInformation.js
@@ -9,6 +9,9 @@ export default function getUserInformation(externalUserId) {
const EVENT_NAME = 'LookUpUserReqMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
check(externalUserId, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js b/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js
index edb96f1924..eb5d12c6c6 100644
--- a/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js
+++ b/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js
@@ -85,6 +85,9 @@ export default function addUserSettings(settings) {
const { meetingId, requesterUserId: userId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(userId, String);
+
let parameters = {};
settings.forEach((el) => {
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/assignPresenter.js b/bigbluebutton-html5/imports/api/users/server/methods/assignPresenter.js
index 7e6c3b67a5..6cf198ec48 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/assignPresenter.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/assignPresenter.js
@@ -11,6 +11,9 @@ export default function assignPresenter(userId) { // TODO-- send username from c
const EVENT_NAME = 'AssignPresenterReqMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
check(userId, String);
const User = Users.findOne({
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/changeRole.js b/bigbluebutton-html5/imports/api/users/server/methods/changeRole.js
index bbb6bd3d31..78705e80d7 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/changeRole.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/changeRole.js
@@ -11,6 +11,8 @@ export default function changeRole(userId, role) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(userId, String);
check(role, String);
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/removeUser.js b/bigbluebutton-html5/imports/api/users/server/methods/removeUser.js
index 4219cfad24..0720559fbc 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/removeUser.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/removeUser.js
@@ -12,6 +12,8 @@ export default function removeUser(userId, banUser) {
const { meetingId, requesterUserId: ejectedBy } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(ejectedBy, String);
check(userId, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/setEmojiStatus.js b/bigbluebutton-html5/imports/api/users/server/methods/setEmojiStatus.js
index dea4d7edb7..d96cd78d96 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/setEmojiStatus.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/setEmojiStatus.js
@@ -11,6 +11,8 @@ export default function setEmojiStatus(userId, status) {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(userId, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/setRandomUser.js b/bigbluebutton-html5/imports/api/users/server/methods/setRandomUser.js
index a0da24a993..34ed860118 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/setRandomUser.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/setRandomUser.js
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function setRandomUser() {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -9,6 +10,9 @@ export default function setRandomUser() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const payload = {
requestedBy: requesterUserId,
};
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/setUserEffectiveConnectionType.js b/bigbluebutton-html5/imports/api/users/server/methods/setUserEffectiveConnectionType.js
index 289e0f1259..f2bfaa949e 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/setUserEffectiveConnectionType.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/setUserEffectiveConnectionType.js
@@ -11,6 +11,8 @@ export default function setUserEffectiveConnectionType(effectiveConnectionType)
const EVENT_NAME = 'ChangeUserEffectiveConnectionMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
check(effectiveConnectionType, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/toggleUserLock.js b/bigbluebutton-html5/imports/api/users/server/methods/toggleUserLock.js
index 3ece68b50c..5e34f4dc61 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/toggleUserLock.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/toggleUserLock.js
@@ -11,6 +11,7 @@ export default function toggleUserLock(userId, lock) {
const { meetingId, requesterUserId: lockedBy } = extractCredentials(this.userId);
+ check(meetingId, String);
check(lockedBy, String);
check(userId, String);
check(lock, Boolean);
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/userActivitySign.js b/bigbluebutton-html5/imports/api/users/server/methods/userActivitySign.js
index 16642f732b..7bf68cd519 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/userActivitySign.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/userActivitySign.js
@@ -3,12 +3,17 @@ import Users from '/imports/api/users';
import RedisPubSub from '/imports/startup/server/redis';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function userActivitySign() {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'UserActivitySignCmdMsg';
const { meetingId, requesterUserId: userId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(userId, String);
+
const payload = {
userId,
};
diff --git a/bigbluebutton-html5/imports/api/users/server/methods/userLeftMeeting.js b/bigbluebutton-html5/imports/api/users/server/methods/userLeftMeeting.js
index 6f28a6066a..dabec39187 100644
--- a/bigbluebutton-html5/imports/api/users/server/methods/userLeftMeeting.js
+++ b/bigbluebutton-html5/imports/api/users/server/methods/userLeftMeeting.js
@@ -2,11 +2,15 @@ import Logger from '/imports/startup/server/logger';
import Users from '/imports/api/users';
import { extractCredentials } from '/imports/api/common/server/helpers';
import ClientConnections from '/imports/startup/server/ClientConnections';
+import { check } from 'meteor/check';
export default function userLeftMeeting() { // TODO-- spread the code to method/modifier/handler
// so we don't update the db in a method
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const selector = {
meetingId,
userId: requesterUserId,
diff --git a/bigbluebutton-html5/imports/api/users/server/publishers.js b/bigbluebutton-html5/imports/api/users/server/publishers.js
index 3cdba628ad..1935728a22 100644
--- a/bigbluebutton-html5/imports/api/users/server/publishers.js
+++ b/bigbluebutton-html5/imports/api/users/server/publishers.js
@@ -3,6 +3,7 @@ import { Meteor } from 'meteor/meteor';
import Logger from '/imports/startup/server/logger';
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
@@ -18,7 +19,7 @@ function currentUser() {
const selector = {
meetingId,
userId: requesterUserId,
- intId: { $exists: true }
+ intId: { $exists: true },
};
const options = {
@@ -57,7 +58,7 @@ function users(role) {
$or: [
{ meetingId },
],
- intId: { $exists: true }
+ intId: { $exists: true },
};
const User = Users.findOne({ userId, meetingId }, { fields: { role: 1 } });
diff --git a/bigbluebutton-html5/imports/api/video-streams/server/methods/userShareWebcam.js b/bigbluebutton-html5/imports/api/video-streams/server/methods/userShareWebcam.js
index 4b67929371..8d21a6182a 100644
--- a/bigbluebutton-html5/imports/api/video-streams/server/methods/userShareWebcam.js
+++ b/bigbluebutton-html5/imports/api/video-streams/server/methods/userShareWebcam.js
@@ -10,9 +10,12 @@ export default function userShareWebcam(stream) {
const EVENT_NAME = 'UserBroadcastCamStartMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+ check(stream, String);
+
Logger.info(`user sharing webcam: ${meetingId} ${requesterUserId}`);
- check(stream, String);
// const actionName = 'joinVideo';
/* TODO throw an error if user has no permission to share webcam
diff --git a/bigbluebutton-html5/imports/api/video-streams/server/methods/userUnshareWebcam.js b/bigbluebutton-html5/imports/api/video-streams/server/methods/userUnshareWebcam.js
index c04d863efd..dd2400bee5 100644
--- a/bigbluebutton-html5/imports/api/video-streams/server/methods/userUnshareWebcam.js
+++ b/bigbluebutton-html5/imports/api/video-streams/server/methods/userUnshareWebcam.js
@@ -10,10 +10,12 @@ export default function userUnshareWebcam(stream) {
const EVENT_NAME = 'UserBroadcastCamStopMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
- Logger.info(`user unsharing webcam: ${meetingId} ${requesterUserId}`);
-
+ check(meetingId, String);
+ check(requesterUserId, String);
check(stream, String);
+ Logger.info(`user unsharing webcam: ${meetingId} ${requesterUserId}`);
+
// const actionName = 'joinVideo';
/* TODO throw an error if user has no permission to share webcam
if (!isAllowedTo(actionName, credentials)) {
diff --git a/bigbluebutton-html5/imports/api/voice-users/server/methods/ejectUserFromVoice.js b/bigbluebutton-html5/imports/api/voice-users/server/methods/ejectUserFromVoice.js
index 1039a83d92..bbf44dd489 100644
--- a/bigbluebutton-html5/imports/api/voice-users/server/methods/ejectUserFromVoice.js
+++ b/bigbluebutton-html5/imports/api/voice-users/server/methods/ejectUserFromVoice.js
@@ -9,6 +9,9 @@ export default function ejectUserFromVoice(userId) {
const EVENT_NAME = 'EjectUserFromVoiceCmdMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
check(userId, String);
const payload = {
diff --git a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllExceptPresenterToggle.js b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllExceptPresenterToggle.js
index c82e280d50..0ee7b2ff9b 100755
--- a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllExceptPresenterToggle.js
+++ b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllExceptPresenterToggle.js
@@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import Meetings from '/imports/api/meetings';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function muteAllExceptPresenterToggle() {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -10,6 +11,9 @@ export default function muteAllExceptPresenterToggle() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const meeting = Meetings.findOne({ meetingId });
const toggleMeetingMuted = !meeting.voiceProp.muteOnStart;
diff --git a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllToggle.js b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllToggle.js
index ecfd3433f9..d818ebcce4 100755
--- a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllToggle.js
+++ b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteAllToggle.js
@@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import Meetings from '/imports/api/meetings';
import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
export default function muteAllToggle() {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -10,6 +11,9 @@ export default function muteAllToggle() {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const meeting = Meetings.findOne({ meetingId });
const toggleMeetingMuted = !meeting.voiceProp.muteOnStart;
diff --git a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js
index 3f89e94ee9..f232bf5890 100644
--- a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js
+++ b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js
@@ -5,6 +5,7 @@ import Users from '/imports/api/users';
import VoiceUsers from '/imports/api/voice-users';
import Meetings from '/imports/api/meetings';
import Logger from '/imports/startup/server/logger';
+import { check } from 'meteor/check';
export default function muteToggle(uId, toggle) {
const REDIS_CONFIG = Meteor.settings.private.redis;
@@ -12,6 +13,10 @@ export default function muteToggle(uId, toggle) {
const EVENT_NAME = 'MuteUserCmdMsg';
const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const userToMute = uId || requesterUserId;
const requester = Users.findOne({
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/handlers/modifyWhiteboardAccess.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/handlers/modifyWhiteboardAccess.js
index 0e21d79c9b..73c01a39cd 100644
--- a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/handlers/modifyWhiteboardAccess.js
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/handlers/modifyWhiteboardAccess.js
@@ -4,7 +4,7 @@ import modifyWhiteboardAccess from '../modifiers/modifyWhiteboardAccess';
export default function handleModifyWhiteboardAccess({ body }, meetingId) {
const { multiUser, whiteboardId } = body;
- check(multiUser, Boolean);
+ check(multiUser, Array);
check(whiteboardId, String);
check(meetingId, String);
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/helpers.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/helpers.js
new file mode 100644
index 0000000000..377403bc3c
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/helpers.js
@@ -0,0 +1,31 @@
+import Users from '/imports/api/users';
+import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user/';
+
+const getMultiUser = (meetingId, whiteboardId) => {
+ const data = WhiteboardMultiUser.findOne(
+ {
+ meetingId,
+ whiteboardId,
+ }, { fields: { multiUser: 1 } },
+ );
+
+ if (!data || !data.multiUser || !Array.isArray(data.multiUser)) return [];
+
+ return data.multiUser;
+};
+
+const getUsers = (meetingId) => {
+ const data = Users.find(
+ { meetingId },
+ { fields: { userId: 1 } },
+ ).fetch();
+
+ if (!data) return [];
+
+ return data.map(user => user.userId);
+};
+
+export {
+ getMultiUser,
+ getUsers,
+};
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods.js
index 6371e1d91c..4400535e31 100644
--- a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods.js
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods.js
@@ -1,6 +1,12 @@
import { Meteor } from 'meteor/meteor';
-import changeWhiteboardAccess from './methods/changeWhiteboardAccess';
+import addGlobalAccess from './methods/addGlobalAccess';
+import addIndividualAccess from './methods/addIndividualAccess';
+import removeGlobalAccess from './methods/removeGlobalAccess';
+import removeIndividualAccess from './methods/removeIndividualAccess';
Meteor.methods({
- changeWhiteboardAccess,
+ addGlobalAccess,
+ addIndividualAccess,
+ removeGlobalAccess,
+ removeIndividualAccess,
});
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/addGlobalAccess.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/addGlobalAccess.js
new file mode 100644
index 0000000000..deb3a81f27
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/addGlobalAccess.js
@@ -0,0 +1,27 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import { getUsers } from '/imports/api/whiteboard-multi-user/server/helpers';
+import { extractCredentials } from '/imports/api/common/server/helpers';
+
+export default function addGlobalAccess(whiteboardId) {
+ const REDIS_CONFIG = Meteor.settings.private.redis;
+ const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+ const EVENT_NAME = 'ModifyWhiteboardAccessPubMsg';
+
+ check(whiteboardId, String);
+
+ const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
+ const multiUser = getUsers(meetingId);
+
+ const payload = {
+ multiUser,
+ whiteboardId,
+ };
+
+ return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+}
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/addIndividualAccess.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/addIndividualAccess.js
new file mode 100644
index 0000000000..9dd55a3e5a
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/addIndividualAccess.js
@@ -0,0 +1,32 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import { getMultiUser } from '/imports/api/whiteboard-multi-user/server/helpers';
+import { extractCredentials } from '/imports/api/common/server/helpers';
+
+export default function addIndividualAccess(whiteboardId, userId) {
+ const REDIS_CONFIG = Meteor.settings.private.redis;
+ const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+ const EVENT_NAME = 'ModifyWhiteboardAccessPubMsg';
+
+ check(whiteboardId, String);
+ check(userId, String);
+
+ const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
+ const multiUser = getMultiUser(meetingId, whiteboardId);
+
+ if (!multiUser.includes(userId)) {
+ multiUser.push(userId);
+
+ const payload = {
+ multiUser,
+ whiteboardId,
+ };
+
+ return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+ }
+}
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/changeWhiteboardAccess.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/removeGlobalAccess.js
similarity index 81%
rename from bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/changeWhiteboardAccess.js
rename to bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/removeGlobalAccess.js
index ffd3b16626..4c4fe8a7fb 100644
--- a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/changeWhiteboardAccess.js
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/removeGlobalAccess.js
@@ -3,18 +3,20 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { extractCredentials } from '/imports/api/common/server/helpers';
-export default function changeWhiteboardAccess(multiUser, whiteboardId) {
+export default function removeGlobalAccess(whiteboardId) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'ModifyWhiteboardAccessPubMsg';
- const { meetingId, requesterUserId } = extractCredentials(this.userId);
-
- check(multiUser, Boolean);
check(whiteboardId, String);
+ const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
const payload = {
- multiUser,
+ multiUser: [],
whiteboardId,
};
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/removeIndividualAccess.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/removeIndividualAccess.js
new file mode 100644
index 0000000000..367c471d69
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/methods/removeIndividualAccess.js
@@ -0,0 +1,30 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import { getMultiUser } from '/imports/api/whiteboard-multi-user/server/helpers';
+import { extractCredentials } from '/imports/api/common/server/helpers';
+
+export default function removeIndividualAccess(whiteboardId, userId) {
+ const REDIS_CONFIG = Meteor.settings.private.redis;
+ const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+ const EVENT_NAME = 'ModifyWhiteboardAccessPubMsg';
+
+ check(whiteboardId, String);
+ check(userId, String);
+
+ const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
+ const multiUser = getMultiUser(meetingId, whiteboardId);
+
+ if (multiUser.includes(userId)) {
+ const payload = {
+ multiUser: multiUser.filter(id => id !== userId),
+ whiteboardId,
+ };
+
+ return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+ }
+}
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/modifiers/modifyWhiteboardAccess.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/modifiers/modifyWhiteboardAccess.js
index 5d1944426a..7d28c0e8d8 100644
--- a/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/modifiers/modifyWhiteboardAccess.js
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/server/modifiers/modifyWhiteboardAccess.js
@@ -5,7 +5,7 @@ import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user/';
export default function modifyWhiteboardAccess(meetingId, whiteboardId, multiUser) {
check(meetingId, String);
check(whiteboardId, String);
- check(multiUser, Boolean);
+ check(multiUser, Array);
const selector = {
meetingId,
diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx
index 4dd0041992..b83d3a5e58 100755
--- a/bigbluebutton-html5/imports/startup/client/base.jsx
+++ b/bigbluebutton-html5/imports/startup/client/base.jsx
@@ -34,6 +34,7 @@ const BREAKOUT_END_NOTIFY_DELAY = 50;
const HTML = document.getElementsByTagName('html')[0];
let breakoutNotified = false;
+let checkedUserSettings = false;
const propTypes = {
subscriptionsReady: PropTypes.bool,
@@ -400,15 +401,22 @@ const BaseContainer = withTracker(() => {
},
});
- if (getFromUserSettings('bbb_show_participants_on_login', Meteor.settings.public.layout.showParticipantsOnLogin) && !deviceInfo.type().isPhone) {
- if (CHAT_ENABLED && getFromUserSettings('bbb_show_public_chat_on_login', !Meteor.settings.public.chat.startClosed)) {
- Session.set('openPanel', 'chat');
- Session.set('idChatOpen', PUBLIC_CHAT_ID);
- } else {
- Session.set('openPanel', 'userlist');
+ if (Session.equals('openPanel', undefined) || Session.equals('subscriptionsReady', true)) {
+ if (!checkedUserSettings) {
+ if (getFromUserSettings('bbb_show_participants_on_login', Meteor.settings.public.layout.showParticipantsOnLogin) && !deviceInfo.type().isPhone) {
+ if (CHAT_ENABLED && getFromUserSettings('bbb_show_public_chat_on_login', !Meteor.settings.public.chat.startClosed)) {
+ Session.set('openPanel', 'chat');
+ Session.set('idChatOpen', PUBLIC_CHAT_ID);
+ } else {
+ Session.set('openPanel', 'userlist');
+ }
+ } else {
+ Session.set('openPanel', '');
+ }
+ if ( Session.equals('subscriptionsReady', true )) {
+ checkedUserSettings = true;
+ }
}
- } else {
- Session.set('openPanel', '');
}
const codeError = Session.get('codeError');
diff --git a/bigbluebutton-html5/imports/startup/client/intl.jsx b/bigbluebutton-html5/imports/startup/client/intl.jsx
index c56fac96ba..235671f081 100644
--- a/bigbluebutton-html5/imports/startup/client/intl.jsx
+++ b/bigbluebutton-html5/imports/startup/client/intl.jsx
@@ -69,75 +69,72 @@ class IntlStartup extends Component {
fetch(url)
.then((response) => {
if (!response.ok) {
- return Promise.reject();
+ return false;
}
-
return response.json();
})
.then(({ normalizedLocale, regionDefaultLocale }) => {
- fetch(`${localesPath}/${DEFAULT_LANGUAGE}.json`)
- .then((response) => {
- if (!response.ok) {
- return Promise.reject();
- }
- return response.json();
- })
- .then((messages) => {
- if (regionDefaultLocale !== '') {
- fetch(`${localesPath}/${regionDefaultLocale}.json`)
- .then((response) => {
- if (!response.ok) {
- return Promise.resolve();
- }
- return response.json();
- })
- .then((regionDefaultMessages) => {
- messages = Object.assign(messages, regionDefaultMessages);
- return messages;
- });
+ const fetchFallbackMessages = new Promise((resolve, reject) => {
+ fetch(`${localesPath}/${DEFAULT_LANGUAGE}.json`)
+ .then((response) => {
+ if (!response.ok) {
+ return reject();
+ }
+ return resolve(response.json());
+ });
+ });
+
+ const fetchRegionMessages = new Promise((resolve) => {
+ if (!regionDefaultLocale) {
+ return resolve(false);
+ }
+ fetch(`${localesPath}/${regionDefaultLocale}.json`)
+ .then((response) => {
+ if (!response.ok) {
+ return resolve(false);
+ }
+ return resolve(response.json());
+ });
+ });
+
+ const fetchSpecificMessages = new Promise((resolve) => {
+ if (!normalizedLocale || normalizedLocale === DEFAULT_LANGUAGE || normalizedLocale === regionDefaultLocale) {
+ return resolve(false);
+ }
+ fetch(`${localesPath}/${normalizedLocale}.json`)
+ .then((response) => {
+ if (!response.ok) {
+ return resolve(false);
+ }
+ return resolve(response.json());
+ });
+ });
+
+ Promise.all([fetchFallbackMessages, fetchRegionMessages, fetchSpecificMessages])
+ .then((values) => {
+ let mergedMessages = Object.assign({}, values[0]);
+
+ if (!values[1] && !values[2]) {
+ normalizedLocale = DEFAULT_LANGUAGE;
+ } else {
+ if (values[1]) {
+ mergedMessages = Object.assign(mergedMessages, values[1]);
+ }
+ if (values[2]) {
+ mergedMessages = Object.assign(mergedMessages, values[2]);
+ }
}
- if (normalizedLocale !== DEFAULT_LANGUAGE && normalizedLocale !== regionDefaultLocale) {
- fetch(`${localesPath}/${normalizedLocale}.json`)
- .then((response) => {
- if (!response.ok) {
- return Promise.reject();
- }
- return response.json();
- })
- .then((localeMessages) => {
- messages = Object.assign(messages, localeMessages);
- return messages;
- })
- .catch(() => {
- normalizedLocale = (regionDefaultLocale) || DEFAULT_LANGUAGE;
- const dasherizedLocale = normalizedLocale.replace('_', '-');
- this.setState({ messages, fetching: false, normalizedLocale: dasherizedLocale }, () => {
- IntlStartup.saveLocale(normalizedLocale);
- });
- });
- }
-
- return messages;
- })
- .then((messages) => {
const dasherizedLocale = normalizedLocale.replace('_', '-');
- this.setState({ messages, fetching: false, normalizedLocale: dasherizedLocale }, () => {
+ this.setState({ messages: mergedMessages, fetching: false, normalizedLocale: dasherizedLocale }, () => {
IntlStartup.saveLocale(dasherizedLocale);
});
})
.catch(() => {
- normalizedLocale = DEFAULT_LANGUAGE;
- const dasherizedLocale = normalizedLocale.replace('_', '-');
- this.setState({ fetching: false, normalizedLocale: dasherizedLocale }, () => {
- IntlStartup.saveLocale(normalizedLocale);
+ this.setState({ fetching: false, normalizedLocale: null }, () => {
+ IntlStartup.saveLocale(DEFAULT_LANGUAGE);
});
});
- })
- .catch(() => {
- this.setState({ fetching: false, normalizedLocale: null }, () => {
- IntlStartup.saveLocale(DEFAULT_LANGUAGE);
- });
});
});
}
diff --git a/bigbluebutton-html5/imports/startup/server/ClientConnections.js b/bigbluebutton-html5/imports/startup/server/ClientConnections.js
index 9bb837f4ae..a8da598c50 100644
--- a/bigbluebutton-html5/imports/startup/server/ClientConnections.js
+++ b/bigbluebutton-html5/imports/startup/server/ClientConnections.js
@@ -3,6 +3,7 @@ import userLeaving from '/imports/api/users/server/methods/userLeaving';
import { extractCredentials } from '/imports/api/common/server/helpers';
import AuthTokenValidation from '/imports/api/auth-token-validation';
import Users from '/imports/api/users';
+import { check } from 'meteor/check';
const { enabled, syncInterval } = Meteor.settings.public.syncUsersWithConnectionManager;
@@ -38,6 +39,9 @@ class ClientConnections {
const { meetingId, requesterUserId: userId } = extractCredentials(sessionId);
+ check(meetingId, String);
+ check(userId, String);
+
if (!meetingId) {
Logger.error('Error on add new client connection. sessionId=${sessionId} connection=${connection.id}',
{ logCode: 'client_connections_add_error_meeting_id_null', extraInfo: { meetingId, userId } }
@@ -88,6 +92,9 @@ class ClientConnections {
getConnectionsForClient(sessionId) {
const { meetingId, requesterUserId: userId } = extractCredentials(sessionId);
+ check(meetingId, String);
+ check(userId, String);
+
return this.connections.get(meetingId)?.get(userId);
}
@@ -108,6 +115,9 @@ class ClientConnections {
Logger.info(`Removing connectionId for user. sessionId=${sessionId} connectionId=${connectionId}`);
const { meetingId, requesterUserId: userId } = extractCredentials(sessionId);
+ check(meetingId, String);
+ check(userId, String);
+
const meetingConnections = this.connections.get(meetingId);
if (meetingConnections?.has(userId)) {
diff --git a/bigbluebutton-html5/imports/startup/server/redis.js b/bigbluebutton-html5/imports/startup/server/redis.js
index a862cfb308..793a398829 100755
--- a/bigbluebutton-html5/imports/startup/server/redis.js
+++ b/bigbluebutton-html5/imports/startup/server/redis.js
@@ -298,7 +298,7 @@ class RedisPubSub {
});
}
}
- // I ignore
+ // ignore
}
} else {
// add to existing queue
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx
index 1a84ba7a5a..85bd4aa972 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx
@@ -117,6 +117,7 @@ class ActionsDropdown extends PureComponent {
handleTakePresenter,
isSharingVideo,
isPollingEnabled,
+ isSelectRandomUserEnabled,
stopExternalVideoShare,
mountModal,
} = this.props;
@@ -188,7 +189,7 @@ class ActionsDropdown extends PureComponent {
/>
)
: null),
- (amIPresenter
+ (amIPresenter && isSelectRandomUserEnabled
? (
)
: null}
-
+ {
+