Merge remote-tracking branch 'bigbluebutton/master' into mobile-no-force-debug

# Conflicts:
#	clients/flash/air-client/src/org/bigbluebutton/air/main/views/LoadingScreenMediator.as
This commit is contained in:
Ghazi Triki 2018-09-13 09:25:40 +01:00
commit 845ffc8343
1468 changed files with 56862 additions and 33791 deletions

View File

@ -0,0 +1,2 @@
Dockerfile

24
akka-bbb-apps/Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM bbb-common-message AS builder
ARG COMMON_VERSION=0.0.1-SNAPSHOT
COPY . /source
RUN cd /source \
&& find -name build.sbt -exec sed -i "s|\(.*org.bigbluebutton.*bbb-common-message[^\"]*\"[ ]*%[ ]*\)\"[^\"]*\"\(.*\)|\1\"$COMMON_VERSION\"\2|g" {} \; \
&& sbt compile
RUN apt-get update \
&& apt-get -y install fakeroot
RUN cd /source \
&& sbt debian:packageBin
# FROM ubuntu:16.04
FROM openjdk:8-jre-slim-stretch
COPY --from=builder /source/target/*.deb /root/
RUN dpkg -i /root/*.deb
CMD ["/usr/share/bbb-apps-akka/bin/bbb-apps-akka"]

View File

@ -17,7 +17,7 @@ object BreakoutRoom {
//| pter=com.google.gson.Gso
//| Output exceeds cutoff limit.
val string = "{\"payload\":{\"redirectJoinUrl\":\"alink\",\"breakoutMeetingId\":\"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\",\"noRedirectJoinUrl\":\"http://bbb.riadvice.com/bigbluebutton/api/join?fullName\u003dOpera\u0026isBreakout\u003dtrue\u0026meetingID\u003d4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\u0026password\u003dmp\u0026redirect\u003dfalse\u0026userID\u003d6pt0vfeaxdze_1-1\u0026checksum\u003d51c4a1398b88170c25f1a71521bca604e784ab23\",\"parentMeetingId\":\"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178\",\"userId\":\"6pt0vfeaxdze_1\"},\"header\":{\"name\":\"BreakoutRoomJoinURL\",\"version\":\"0.0.1\",\"current_time\":1479728673586,\"timestamp\":8549632}}"
val string = "{\"payload\":{\"redirectJoinUrl\":\"alink\",\"breakoutMeetingId\":\"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\",\"redirectToHtml5JoinUrl\":\"http://bbb.riadvice.com/bigbluebutton/api/join?fullName\u003dOpera\u0026isBreakout\u003dtrue\u0026meetingID\u003d4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\u0026password\u003dmp\u0026redirect\u003dfalse\u0026userID\u003d6pt0vfeaxdze_1-1\u0026checksum\u003d51c4a1398b88170c25f1a71521bca604e784ab23\",\"parentMeetingId\":\"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178\",\"userId\":\"6pt0vfeaxdze_1\"},\"header\":{\"name\":\"BreakoutRoomJoinURL\",\"version\":\"0.0.1\",\"current_time\":1479728673586,\"timestamp\":8549632}}"
//> string : String = {"payload":{"redirectJoinUrl":"alink","breakoutMeetingId"
//| :"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031","noRedirectJoinUrl
//| ":"http://bbb.riadvice.com/bigbluebutton/api/join?fullName=Opera&isBreakout=
@ -35,5 +35,5 @@ object BreakoutRoom {
println(brjum.payload.parentMeetingId) //> 183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178
println(brjum.payload.breakoutMeetingId) //> 4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031
println(brjum.payload.redirectJoinURL) //> null
println(brjum.payload.noRedirectJoinURL) //> null
println(brjum.payload.redirectToHtml5JoinURL) //> null
}

View File

@ -35,13 +35,6 @@ redis {
keyExpiry=1209600
}
inactivity {
# time in seconds
deadline=7200
# inactivity warning message
timeLeft=300
}
expire {
# time in seconds
lastUserLeft = 60
@ -82,6 +75,8 @@ services {
apps {
checkPermissions = true
endMeetingWhenNoMoreAuthedUsers = false
endMeetingWhenNoMoreAuthedUsersAfterMinutes = 2
}
voiceConf {
@ -91,3 +86,8 @@ voiceConf {
recording {
chapterBreakLengthInMinutes = 180
}
whiteboard {
multiUserDefault = false
}

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{ISO8601} %-5level %logger{36} - %msg%n</pattern>
</encoder>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/bbb-apps-akka.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/bbb-apps-akka.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 30 days worth of history -->
<MaxHistory>5</MaxHistory>
<!-- keep 14 days worth of history -->
<MaxHistory>14</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd HH:mm:ss,SSSXXX"} [%thread] %-5level %logger{35} - %msg%n</Pattern>
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>

View File

@ -21,9 +21,6 @@ trait SystemConfiguration {
lazy val red5DeskShareIP = Try(config.getString("red5.deskshareip")).getOrElse("127.0.0.1")
lazy val red5DeskShareApp = Try(config.getString("red5.deskshareapp")).getOrElse("")
lazy val inactivityDeadline = Try(config.getInt("inactivity.deadline")).getOrElse(2 * 3600) // 2 hours
lazy val inactivityTimeLeft = Try(config.getInt("inactivity.timeLeft")).getOrElse(5 * 60) // 5 minutes
lazy val expireLastUserLeft = Try(config.getInt("expire.lastUserLeft")).getOrElse(60) // 1 minute
lazy val expireNeverJoined = Try(config.getInt("expire.neverJoined")).getOrElse(5 * 60) // 5 minutes
@ -63,4 +60,9 @@ trait SystemConfiguration {
lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings")
lazy val recordingChapterBreakLenghtInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(180)
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)
}

View File

@ -26,6 +26,8 @@ case class MonitorNumberOfUsersInternalMsg(meetingID: String) extends InMessage
*/
case class SendTimeRemainingAuditInternalMsg(meetingId: String) extends InMessage
case class SendRecordingTimerInternalMsg(meetingId: String) extends InMessage
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
case class DestroyMeetingInternalMsg(meetingId: String) extends InMessage

View File

@ -9,10 +9,11 @@ object BreakoutModel {
externalId: String,
name: String,
sequence: Integer,
freeJoin: Boolean,
voiceConf: String,
assignedUsers: Vector[String]
): BreakoutRoom2x = {
new BreakoutRoom2x(id, externalId, name, parentId, sequence, voiceConf, assignedUsers, Vector(), Vector(), None, false)
new BreakoutRoom2x(id, externalId, name, parentId, sequence, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false)
}
}

View File

@ -7,8 +7,9 @@ import scala.collection.immutable.HashMap
import scala.collection.JavaConverters._
import org.bigbluebutton.common2.msgs.AnnotationVO
import org.bigbluebutton.core.apps.whiteboard.Whiteboard
import org.bigbluebutton.SystemConfiguration
class WhiteboardModel {
class WhiteboardModel extends SystemConfiguration {
private var _whiteboards = new HashMap[String, Whiteboard]()
private def saveWhiteboard(wb: Whiteboard) {
@ -24,7 +25,7 @@ class WhiteboardModel {
}
private def createWhiteboard(wbId: String): Whiteboard = {
new Whiteboard(wbId, false, System.currentTimeMillis(), 0, new HashMap[String, List[AnnotationVO]]())
new Whiteboard(wbId, multiUserWhiteboardDefault, System.currentTimeMillis(), 0, new HashMap[String, List[AnnotationVO]]())
}
private def getAnnotationsByUserId(wb: Whiteboard, id: String): List[AnnotationVO] = {

View File

@ -56,10 +56,11 @@ object BreakoutRoomsUtil {
"userID" -> urlEncode(userId),
"isBreakout" -> urlEncode(isBreakout.toString()),
"meetingID" -> urlEncode(breakoutMeetingId),
"password" -> urlEncode(password)
"password" -> urlEncode(password),
"redirect" -> urlEncode("true")
)
(params + ("redirect" -> urlEncode("true")), params + ("redirect" -> urlEncode("false")))
(params, params + ("joinViaHtml5" -> urlEncode("true")))
}
def sortParams(params: collection.immutable.Map[String, String]): SortedSet[String] = {

View File

@ -17,37 +17,37 @@ trait BreakoutHdlrHelpers extends SystemConfiguration {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
apiCall = "join"
(redirectParams, noRedirectParams) = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true,
(redirectParams, redirectToHtml5Params) = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true,
externalMeetingId, liveMeeting.props.password.moderatorPass)
// We generate a first url with redirect -> true
redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams)
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret))
// We generate a second url with redirect -> false
noRedirectBaseString = BreakoutRoomsUtil.createBaseString(noRedirectParams)
noRedirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, noRedirectBaseString,
BreakoutRoomsUtil.calculateChecksum(apiCall, noRedirectBaseString, bbbWebSharedSecret))
// We generate a second url with redirect -> true and joinViaHtml5 -> true
redirectToHtml5BaseString = BreakoutRoomsUtil.createBaseString(redirectToHtml5Params)
redirectToHtml5JoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectToHtml5BaseString,
BreakoutRoomsUtil.calculateChecksum(apiCall, redirectToHtml5BaseString, bbbWebSharedSecret))
} yield {
sendJoinURLMsg(liveMeeting.props.meetingProp.intId, breakoutId, externalMeetingId,
userId, redirectJoinURL, noRedirectJoinURL)
userId, redirectJoinURL, redirectToHtml5JoinURL)
}
}
def sendJoinURLMsg(meetingId: String, breakoutId: String, externalId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String): Unit = {
userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String): Unit = {
def build(meetingId: String, breakoutId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String): BbbCommonEnvCoreMsg = {
userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(BreakoutRoomJoinURLEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(BreakoutRoomJoinURLEvtMsg.NAME, meetingId, userId)
val body = BreakoutRoomJoinURLEvtMsgBody(meetingId, breakoutId, externalId,
userId, redirectJoinURL, noRedirectJoinURL)
userId, redirectJoinURL, redirectToHtml5JoinURL)
val event = BreakoutRoomJoinURLEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val msgEvent = build(meetingId, breakoutId, userId, redirectJoinURL, noRedirectJoinURL)
val msgEvent = build(meetingId, breakoutId, userId, redirectJoinURL, redirectToHtml5JoinURL)
outGW.send(msgEvent)
}

View File

@ -61,7 +61,7 @@ trait BreakoutRoomCreatedMsgHdlr extends BreakoutHdlrHelpers {
def sendBreakoutRoomsList(breakoutModel: BreakoutModel): BreakoutModel = {
val breakoutRooms = breakoutModel.rooms.values.toVector map { r =>
new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence)
new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence, r.freeJoin)
}
log.info("Sending breakout rooms list to {} with containing {} room(s)", liveMeeting.props.meetingProp.intId, breakoutRooms.length)
@ -88,7 +88,7 @@ trait BreakoutRoomCreatedMsgHdlr extends BreakoutHdlrHelpers {
BbbCommonEnvCoreMsg(envelope, event)
}
val breakoutInfo = BreakoutRoomInfo(room.name, room.externalId, room.id, room.sequence)
val breakoutInfo = BreakoutRoomInfo(room.name, room.externalId, room.id, room.sequence, room.freeJoin)
val event = build(liveMeeting.props.meetingProp.intId, breakoutInfo)
outGW.send(event)

View File

@ -28,7 +28,7 @@ trait BreakoutRoomsListMsgHdlr {
breakoutModel <- state.breakout
} yield {
val rooms = breakoutModel.rooms.values.toVector map { r =>
new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence)
new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence, r.freeJoin)
}
val ready = breakoutModel.hasAllStarted()
broadcastEvent(rooms, ready)

View File

@ -46,7 +46,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
val (internalId, externalId) = BreakoutRoomsUtil.createMeetingIds(liveMeeting.props.meetingProp.intId, i)
val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i)
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, voiceConf, room.users)
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.freeJoin, voiceConf, room.users)
rooms = rooms + (breakout.id -> breakout)
}
@ -55,6 +55,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
breakout.id, breakout.name,
liveMeeting.props.meetingProp.intId,
breakout.sequence,
breakout.freeJoin,
breakout.voiceConf,
msg.body.durationInMinutes,
liveMeeting.props.password.moderatorPass,

View File

@ -6,6 +6,7 @@ class ChatApp2x(implicit val context: ActorContext)
extends GetChatHistoryReqMsgHdlr
with SendPublicMessagePubMsgHdlr
with SendPrivateMessagePubMsgHdlr
with ClearPublicChatHistoryPubMsgHdlr {
with ClearPublicChatHistoryPubMsgHdlr
with UserTypingPubMsgHdlr {
}

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.core.apps.chat
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
trait UserTypingPubMsgHdlr extends LogHelper {
def handle(msg: UserTypingPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: UserTypingPubMsg): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(UserTypingEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UserTypingEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = UserTypingEvtMsgBody(msg.body.chatId, msg.header.userId)
val event = UserTypingEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
broadcastEvent(msg)
}
}

View File

@ -0,0 +1,38 @@
package org.bigbluebutton.core.apps.groupchats
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.GroupChat
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.models.SystemUser
trait CreateDefaultPublicGroupChat {
this: GroupChatHdlrs =>
def handleCreateDefaultPublicGroupChat(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
val groupChat: GroupChat = GroupChatApp.createDefaultPublicGroupChat()
def buildGroupChatCreatedEvtMsg(meetingId: String, userId: String, gc: GroupChat): BbbCommonEnvCoreMsg = {
val correlationId = "SYSTEM-" + System.currentTimeMillis()
val msgs = gc.msgs.map(m => GroupChatApp.toMessageToUser(m))
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(GroupChatCreatedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(GroupChatCreatedEvtMsg.NAME, meetingId, userId)
val body = GroupChatCreatedEvtMsgBody(correlationId, gc.id, gc.createdBy, gc.name, gc.access, gc.users, msgs)
val event = GroupChatCreatedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val respMsg = buildGroupChatCreatedEvtMsg(
liveMeeting.props.meetingProp.intId,
SystemUser.ID,
groupChat
)
bus.outGW.send(respMsg)
val groupChats = state.groupChats.add(groupChat)
state.update(groupChats)
}
}

View File

@ -44,12 +44,9 @@ object GroupChatApp {
}
}
def createDefaultPublicGroupChat(chatId: String, state: MeetingState2x): MeetingState2x = {
def createDefaultPublicGroupChat(): GroupChat = {
val createBy = GroupChatUser(SystemUser.ID, SystemUser.ID)
val defaultPubGroupChat = GroupChatFactory.create(chatId, chatId,
GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty)
val groupChats = state.groupChats.add(defaultPubGroupChat)
state.update(groupChats)
GroupChatFactory.create(MAIN_PUBLIC_CHAT, MAIN_PUBLIC_CHAT, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty)
}
def createTestPublicGroupChat(state: MeetingState2x): MeetingState2x = {
@ -60,6 +57,10 @@ object GroupChatApp {
state.update(groupChats)
}
def getAllGroupChatsInMeeting(state: MeetingState2x): Vector[GroupChat] = {
state.groupChats.getAllGroupChatsInMeeting()
}
def genTestChatMsgHistory(chatId: String, state: MeetingState2x, userId: String, liveMeeting: LiveMeeting): MeetingState2x = {
def addH(state: MeetingState2x, userId: String, liveMeeting: LiveMeeting, msg: GroupChatMsgFromUser): MeetingState2x = {
val newState = for {

View File

@ -5,9 +5,11 @@ import akka.event.Logging
class GroupChatHdlrs(implicit val context: ActorContext)
extends CreateGroupChatReqMsgHdlr
with CreateDefaultPublicGroupChat
with GetGroupChatMsgsReqMsgHdlr
with GetGroupChatsReqMsgHdlr
with SendGroupChatMessageMsgHdlr {
with SendGroupChatMessageMsgHdlr
with SyncGetGroupChatsInfoMsgHdlr {
val log = Logging(context.system, getClass)
}

View File

@ -0,0 +1,53 @@
package org.bigbluebutton.core.apps.groupchats
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.LiveMeeting
trait SyncGetGroupChatsInfoMsgHdlr {
this: GroupChatHdlrs =>
def handleSyncGetGroupChatsInfo(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetGroupChatsRespMsg(allChats: Vector[GroupChatInfo]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatsRespMsgBody(allChats)
val event = SyncGetGroupChatsRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildSyncGetGroupChatMsgsRespMsg(msgs: Vector[GroupChatMsgToUser], chatId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatMsgsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatMsgsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatMsgsRespMsgBody(chatId, msgs)
val event = SyncGetGroupChatMsgsRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
// fetching all the group chats in the meeting
val chats = GroupChatApp.getAllGroupChatsInMeeting(state)
// mapping group chats, while fetching and publishing messages for each group chat
val allChats = chats map (pc => {
val msgs = pc.msgs.toVector map (m => GroupChatMsgToUser(m.id, m.createdOn, m.correlationId,
m.sender, m.color, m.message))
val respMsg = buildSyncGetGroupChatMsgsRespMsg(msgs, pc.id)
bus.outGW.send(respMsg)
GroupChatInfo(pc.id, pc.name, pc.access, pc.createdBy, pc.users)
})
// publishing a message with the group chat info
val respMsg = buildSyncGetGroupChatsRespMsg(allChats)
bus.outGW.send(respMsg)
state
}
}

View File

@ -5,6 +5,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.core.models.Users2x
trait RespondToPollReqMsgHdlr {
this: PollApp2x =>
@ -34,12 +35,29 @@ trait RespondToPollReqMsgHdlr {
bus.outGW.send(msgEvent)
}
def broadcastUserRespondedToPollRespMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int, sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
val body = UserRespondedToPollRespMsgBody(pollId, msg.header.userId, answerId)
val event = UserRespondedToPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, msg.body.answerId, liveMeeting)
} yield {
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
broadcastUserRespondedToPollRecordMsg(msg, pollId, msg.body.answerId)
for {
presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield {
broadcastUserRespondedToPollRespMsg(msg, pollId, msg.body.answerId, presenter.intId)
}
}
}
}

View File

@ -0,0 +1,27 @@
package org.bigbluebutton.core.apps.presentationpod
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.PresentationPod
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.models.SystemUser
trait CreateDefaultPresentationPod {
this: PresentationPodHdlrs =>
def handleCreateDefaultPresentationPod(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
val SYSTEM_ID = SystemUser.ID
val resultPod: PresentationPod = PresentationPodsApp.createDefaultPresentationPod()
val respMsg = MsgBuilder.buildCreateNewPresentationPodEvtMsg(
liveMeeting.props.meetingProp.intId,
resultPod.currentPresenter,
resultPod.id,
SYSTEM_ID
)
bus.outGW.send(respMsg)
val pods = state.presentationPodManager.addPod(resultPod)
state.update(pods)
}
}

View File

@ -6,6 +6,7 @@ import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.PresentationPod
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait CreateNewPresentationPodPubMsgHdlr extends RightsManagementTrait {
this: PresentationPodHdlrs =>
@ -22,22 +23,14 @@ trait CreateNewPresentationPodPubMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
state
} else {
def buildCreateNewPresentationPodEvtMsg(meetingId: String, currentPresenterId: String, podId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, msg.header.userId)
val envelope = BbbCoreEnvelope(CreateNewPresentationPodEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(CreateNewPresentationPodEvtMsg.NAME, meetingId, msg.header.userId)
val body = CreateNewPresentationPodEvtMsgBody(currentPresenterId, podId)
val event = CreateNewPresentationPodEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val resultPod: PresentationPod = PresentationPodsApp.createPresentationPod(msg.header.userId)
val respMsg = buildCreateNewPresentationPodEvtMsg(
val respMsg = MsgBuilder.buildCreateNewPresentationPodEvtMsg(
liveMeeting.props.meetingProp.intId,
resultPod.currentPresenter, resultPod.id
resultPod.currentPresenter,
resultPod.id,
msg.header.userId
)
bus.outGW.send(respMsg)

View File

@ -16,20 +16,7 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
liveMeeting: LiveMeeting, bus: MessageBus
): MeetingState2x = {
def broadcastPresentationConversionCompletedEvtMsg(podId: String, userId: String, messageKey: String,
code: String, presentation: PresentationVO): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId, userId
)
val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PresentationConversionCompletedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
val body = PresentationConversionCompletedEvtMsgBody(podId, messageKey, code, presentation)
val event = PresentationConversionCompletedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val meetingId = liveMeeting.props.meetingProp.intId
val pages = new collection.mutable.HashMap[String, PageVO]
@ -39,15 +26,20 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
pages += page.id -> page
}
val pres = new PresentationInPod(msg.body.presentation.id, msg.body.presentation.name, msg.body.presentation.current,
pages.toMap, msg.body.presentation.downloadable)
val downloadable = msg.body.presentation.downloadable
val presentationId = msg.body.presentation.id
val pres = new PresentationInPod(presentationId, msg.body.presentation.name, msg.body.presentation.current,
pages.toMap, downloadable)
val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres)
val podId = msg.body.podId
val newState = for {
pod <- PresentationPodsApp.getPresentationPod(state, podId)
} yield {
broadcastPresentationConversionCompletedEvtMsg(pod.id, msg.header.userId, msg.body.messageKey, msg.body.code, presVO)
PresentationSender.broadcastPresentationConversionCompletedEvtMsg(bus, meetingId,
pod.id, msg.header.userId, msg.body.messageKey, msg.body.code, presVO)
PresentationSender.broadcastSetPresentationDownloadableEvtMsg(bus, meetingId, pod.id,
msg.header.userId, presentationId, downloadable, pres.name)
var pods = state.presentationPodManager.addPod(pod)
pods = pods.addPresentationToPod(pod.id, pres)

View File

@ -5,6 +5,7 @@ import akka.event.Logging
class PresentationPodHdlrs(implicit val context: ActorContext)
extends CreateNewPresentationPodPubMsgHdlr
with CreateDefaultPresentationPod
with GetAllPresentationPodsReqMsgHdlr
with SetCurrentPresentationPubMsgHdlr
with PresentationConversionCompletedSysPubMsgHdlr

View File

@ -11,9 +11,8 @@ object PresentationPodsApp {
PresentationPodFactory.create(creatorId)
}
def createDefaultPresentationPod(state: MeetingState2x): MeetingState2x = {
val podManager = state.presentationPodManager.addPod(PresentationPodFactory.createDefaultPod())
state.update(podManager)
def createDefaultPresentationPod(): PresentationPod = {
PresentationPodFactory.createDefaultPod()
}
def removePresentationPod(state: MeetingState2x, podId: String): MeetingState2x = {

View File

@ -0,0 +1,47 @@
package org.bigbluebutton.core.apps.presentationpod
import org.bigbluebutton.common2.domain.PresentationVO
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
object PresentationSender {
def broadcastSetPresentationDownloadableEvtMsg(
bus: MessageBus,
meetingId: String,
podId: String, userId: String,
presentationId: String,
downloadable: Boolean,
presFilename: String
): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
meetingId, userId
)
val envelope = BbbCoreEnvelope(SetPresentationDownloadableEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SetPresentationDownloadableEvtMsg.NAME, meetingId, userId)
val body = SetPresentationDownloadableEvtMsgBody(podId, presentationId, downloadable, presFilename)
val event = SetPresentationDownloadableEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
def broadcastPresentationConversionCompletedEvtMsg(
bus: MessageBus,
meetingId: String,
podId: String, userId: String, messageKey: String,
code: String, presentation: PresentationVO
): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
meetingId, userId
)
val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PresentationConversionCompletedEvtMsg.NAME, meetingId, userId)
val body = PresentationConversionCompletedEvtMsgBody(podId, messageKey, code, presentation)
val event = PresentationConversionCompletedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
}

View File

@ -14,26 +14,14 @@ trait SetPresentationDownloadablePubMsgHdlr extends RightsManagementTrait {
liveMeeting: LiveMeeting, bus: MessageBus
): MeetingState2x = {
val meetingId = liveMeeting.props.meetingProp.intId
if (filterPresentationMessage(liveMeeting.users2x, msg.header.userId) &&
permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to remove presentation from meeting."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
state
} else {
def broadcastSetPresentationDownloadableEvtMsg(podId: String, userId: String, presentationId: String, downloadable: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId, userId
)
val envelope = BbbCoreEnvelope(SetPresentationDownloadableEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SetPresentationDownloadableEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
val body = SetPresentationDownloadableEvtMsgBody(podId, presentationId, downloadable)
val event = SetPresentationDownloadableEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val podId = msg.body.podId
val presentationId = msg.body.presentationId
@ -41,8 +29,10 @@ trait SetPresentationDownloadablePubMsgHdlr extends RightsManagementTrait {
val newState = for {
pod <- PresentationPodsApp.getPresentationPod(state, podId)
pres <- pod.getPresentation(presentationId)
} yield {
broadcastSetPresentationDownloadableEvtMsg(pod.id, msg.header.userId, presentationId, downloadable)
PresentationSender.broadcastSetPresentationDownloadableEvtMsg(bus, meetingId, pod.id,
msg.header.userId, presentationId, downloadable, pres.name)
val pods = state.presentationPodManager.setPresentationDownloadableInPod(pod.id, presentationId, downloadable)
state.update(pods)

View File

@ -14,7 +14,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
def handleSetLockSettings(msg: ChangeLockSettingsInMeetingCmdMsg): Unit = {
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to change lock settings"
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models.{ Roles, Users2x }
import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, Users2x }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
@ -20,8 +20,17 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
for {
uvo <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
if (msg.body.role == Roles.MODERATOR_ROLE && uvo.authed) {
// Promote only authenticated users.
val userRole = if (uvo.role == Roles.MODERATOR_ROLE) "MODERATOR" else "VIEWER"
for {
// Update guest from waiting list
u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers)
} yield {
RegisteredUsers.updateUserRole(liveMeeting.registeredUsers, u, userRole)
}
if (msg.body.role == Roles.MODERATOR_ROLE && !uvo.guest) {
// Promote non-guest users.
Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
msg.body.changedBy, "MODERATOR")

View File

@ -12,10 +12,10 @@ trait GetRecordingStatusReqMsgHdlr {
def handleGetRecordingStatusReqMsg(msg: GetRecordingStatusReqMsg) {
def buildGetRecordingStatusRespMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
def buildGetRecordingStatusRespMsg(meetingId: String, userId: String, recorded: Boolean, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetRecordingStatusRespMsg.NAME, routing)
val body = GetRecordingStatusRespMsgBody(recording, userId)
val body = GetRecordingStatusRespMsgBody(recorded, recording, userId)
val header = BbbClientMsgHeader(GetRecordingStatusRespMsg.NAME, meetingId, userId)
val event = GetRecordingStatusRespMsg(header, body)
@ -23,7 +23,7 @@ trait GetRecordingStatusReqMsgHdlr {
}
val event = buildGetRecordingStatusRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy,
MeetingStatus2x.isRecording(liveMeeting.status))
liveMeeting.props.recordProp.record, MeetingStatus2x.isRecording(liveMeeting.status))
outGW.send(event)
}
}

View File

@ -34,9 +34,9 @@ trait MuteUserCmdMsgHdlr extends RightsManagementTrait {
requester <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
u <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
} yield {
if (requester.role != Roles.MODERATOR_ROLE && permissions.disableMic &&
if (requester.role != Roles.MODERATOR_ROLE && permissions.disableMic && u.muted &&
msg.body.userId == msg.header.userId) {
// muting/unmuting self while not moderator and mic disabled. Do not allow.
// unmuting self while not moderator and mic disabled. Do not allow.
} else {
if (u.muted != msg.body.mute) {
log.info("Send mute user request. meetingId=" + meetingId + " userId=" + u.intId + " user=" + u)

View File

@ -0,0 +1,46 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait RecordAndClearPreviousMarkersCmdMsgHdlr {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleRecordAndClearPreviousMarkersCmdMsg(msg: RecordAndClearPreviousMarkersCmdMsg, state: MeetingState2x): MeetingState2x = {
log.info("Set a new recording marker and clear previous ones. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording)
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
// Do not allow stop recording and clear previous markers
if (liveMeeting.props.recordProp.allowStartStopRecording &&
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
MeetingStatus2x.recordingStarted(liveMeeting.status)
val tracker = state.recordingTracker.resetTimer(TimeUtil.timeNowInMs())
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
outGW.send(event)
outGW.send(MsgBuilder.buildRecordStatusResetSysMsg(liveMeeting.props.meetingProp.intId, msg.body.recording, msg.body.setBy))
state.update(tracker)
} else {
state
}
}
}

View File

@ -25,8 +25,6 @@ trait RegisterUserReqMsgHdlr {
val guestPolicy = liveMeeting.guestsWaiting.getGuestPolicy().policy
val guestStatus = msg.body.guestStatus
println("****** GUEST POLICY = " + guestPolicy + " guestStatus = " + guestStatus)
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
msg.body.name, msg.body.role, msg.body.authToken,
msg.body.avatarURL, msg.body.guest, msg.body.authed, guestStatus)

View File

@ -0,0 +1,43 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.MeetingStatus2x
trait SendRecordingTimerInternalMsgHdlr {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleSendRecordingTimerInternalMsg(msg: SendRecordingTimerInternalMsg, state: MeetingState2x): MeetingState2x = {
def buildUpdateRecordingTimerEvtMsg(meetingId: String, recordingTime: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(UpdateRecordingTimerEvtMsg.NAME, routing)
val body = UpdateRecordingTimerEvtMsgBody(recordingTime)
val header = BbbClientMsgHeader(UpdateRecordingTimerEvtMsg.NAME, meetingId, "not-used")
val event = UpdateRecordingTimerEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
var newDuration = 0L
if (MeetingStatus2x.isRecording(liveMeeting.status)) {
newDuration = TimeUtil.timeNowInMs()
val tracker = state.recordingTracker.udpateCurrentDuration(newDuration)
val recordingTime = TimeUtil.millisToSeconds(tracker.recordingDuration())
val event = buildUpdateRecordingTimerEvtMsg(liveMeeting.props.meetingProp.intId, recordingTime)
outGW.send(event)
state.update(tracker)
} else {
state
}
}
}

View File

@ -1,8 +1,12 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
@ -11,13 +15,24 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg) {
def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg, state: MeetingState2x): MeetingState2x = {
log.info("Change recording status. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording)
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to clear chat in meeting."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
state
} else {
if (liveMeeting.props.recordProp.allowStartStopRecording &&
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
@ -29,17 +44,21 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
outGW.send(event)
var newState = state
if (MeetingStatus2x.isRecording(liveMeeting.status)) {
val tracker = state.recordingTracker.startTimer(TimeUtil.timeNowInMs())
newState = state.update(tracker)
} else {
val tracker = state.recordingTracker.pauseTimer(TimeUtil.timeNowInMs())
newState = state.update(tracker)
}
eventBus.publish(BigBlueButtonEvent(liveMeeting.props.meetingProp.intId, SendRecordingTimerInternalMsg(liveMeeting.props.meetingProp.intId)))
newState
} else {
state
}
}
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}
}

View File

@ -19,9 +19,20 @@ trait SyncGetUsersMeetingRespMsgHdlr {
val users = Users2x.findAll(liveMeeting.users2x)
val webUsers = users.map { u =>
WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role,
guest = u.guest, authed = u.authed, guestStatus = u.guestStatus, emoji = u.emoji,
locked = u.locked, presenter = u.presenter, avatar = u.avatar)
WebUser(
intId = u.intId,
extId = u.extId,
name = u.name,
role = u.role,
guest = u.guest,
authed = u.authed,
guestStatus = u.guestStatus,
emoji = u.emoji,
locked = u.locked,
presenter = u.presenter,
avatar = u.avatar,
clientType = u.clientType
)
}
val body = SyncGetUsersMeetingRespMsgBody(webUsers)

View File

@ -0,0 +1,17 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserActivitySignCmdMsg
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.MeetingActor
trait UserActivitySignCmdMsgHdlr {
this: MeetingActor =>
def handleUserActivitySignCmdMsg(msg: UserActivitySignCmdMsg): Unit = {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
Users2x.updateLastUserActivity(liveMeeting.users2x, user)
}
}
}

View File

@ -18,15 +18,6 @@ trait UserBroadcastCamStopMsgHdlr {
}
}
def handleUserBroadcastCamStopMsg(userId: String): Unit = {
for {
uvo <- Webcams.findWebcamsForUser(liveMeeting.webcams, userId)
_ <- Webcams.removeWebcamBroadcastStream(liveMeeting.webcams, uvo.streamId)
} yield {
broadcastUserBroadcastCamStoppedEvtMsg(uvo.streamId, userId)
}
}
def broadcastUserBroadcastCamStoppedEvtMsg(streamId: String, userId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, userId)
val envelope = BbbCoreEnvelope(UserBroadcastCamStoppedEvtMsg.NAME, routing)

View File

@ -14,23 +14,11 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with Breako
def handleUserJoinMeetingAfterReconnectReqMsg(msg: UserJoinMeetingAfterReconnectReqMsg, state: MeetingState2x): MeetingState2x = {
val newState = userJoinMeeting(outGW, msg.body.authToken, liveMeeting, state)
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
if (liveMeeting.props.meetingProp.isBreakout) {
updateParentMeetingWithUsers()
}
/*
* *
* // recover voice user
* for {
* vu <- VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, msg.body.userId)
* } yield {
* handleUserJoinedVoiceConfEvtMsg(liveMeeting.props.voiceProp.voiceConf, vu.intId, vu.voiceUserId, vu.callingWith, vu.callerName, vu.callerNum, vu.muted, vu.talking)
* }
*
* newState
*/
newState
}

View File

@ -13,7 +13,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers with BreakoutHdlrHelpers
val outGW: OutMsgRouter
def handleUserJoinMeetingReqMsg(msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = {
val newState = userJoinMeeting(outGW, msg.body.authToken, liveMeeting, state)
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
if (liveMeeting.props.meetingProp.isBreakout) {
updateParentMeetingWithUsers()

View File

@ -1,11 +1,14 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.domain.MeetingStatus
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.presentationpod.PresentationPodsApp
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserLeaveReqMsgHdlr {
@ -35,11 +38,13 @@ trait UserLeaveReqMsgHdlr {
} yield {
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u)
// stop the webcams of a user leaving
handleUserBroadcastCamStopMsg(msg.body.userId)
val authedUsers = Users2x.findAllAuthedUsers(liveMeeting.users2x)
if (u.authed && authedUsers.isEmpty) {
MeetingStatus2x.setLastAuthedUserLeftOn(liveMeeting.status)
}
captionApp2x.handleUserLeavingMsg(msg.body.userId, liveMeeting, msgBus)
stopAutoStartedRecording()
stopRecordingIfAutoStart2x(outGW, liveMeeting, state)
// send a user left event for the clients to update
val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId)

View File

@ -129,6 +129,8 @@ class UsersApp(
with LogoutAndEndMeetingCmdMsgHdlr
with MeetingActivityResponseCmdMsgHdlr
with SetRecordingStatusCmdMsgHdlr
with RecordAndClearPreviousMarkersCmdMsgHdlr
with SendRecordingTimerInternalMsgHdlr
with UpdateWebcamsOnlyForModeratorCmdMsgHdlr
with GetRecordingStatusReqMsgHdlr
with GetWebcamsOnlyForModeratorReqMsgHdlr

View File

@ -66,7 +66,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
val webUsers = users.map { u =>
WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role,
guest = u.guest, authed = u.authed, guestStatus = u.guestStatus, emoji = u.emoji,
locked = u.locked, presenter = u.presenter, avatar = u.avatar)
locked = u.locked, presenter = u.presenter, avatar = u.avatar, clientType = u.clientType)
}
val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers)

View File

@ -0,0 +1,34 @@
package org.bigbluebutton.core.apps.voice
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.VoiceUsers
import org.bigbluebutton.core.domain.MeetingState2x
trait SyncGetVoiceUsersMsgHdlr {
this: BaseMeetingActor =>
def handleSyncGetVoiceUsersMsg(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetVoiceUsersRespMsg(): BbbCommonEnvCoreMsg = {
val voiceUsers = VoiceUsers.findAll(liveMeeting.voiceUsers).map { u =>
VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName,
callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
}
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetVoiceUsersRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetVoiceUsersRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetVoiceUsersRespMsgBody(voiceUsers)
val event = SyncGetVoiceUsersRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val respMsg = buildSyncGetVoiceUsersRespMsg()
bus.outGW.send(respMsg)
state
}
}

View File

@ -9,7 +9,8 @@ trait VoiceApp2x extends UserJoinedVoiceConfEvtMsgHdlr
with UserMutedInVoiceConfEvtMsgHdlr
with UserTalkingInVoiceConfEvtMsgHdlr
with RecordingStartedVoiceConfEvtMsgHdlr
with VoiceConfRunningEvtMsgHdlr {
with VoiceConfRunningEvtMsgHdlr
with SyncGetVoiceUsersMsgHdlr {
this: MeetingActor =>
}

View File

@ -6,6 +6,7 @@ case class BreakoutRoom2x(
name: String,
parentId: String,
sequence: Int,
freeJoin: Boolean,
voiceConf: String,
assignedUsers: Vector[String],
users: Vector[BreakoutUser],

View File

@ -20,7 +20,9 @@ case class MeetingInactivityTracker(
}
def hasRecentActivity(nowInMs: Long): Boolean = {
nowInMs - lastActivityTimestampInMs < maxInactivityTimeoutInMs - warningBeforeMaxInMs
val left = nowInMs - lastActivityTimestampInMs
val right = maxInactivityTimeoutInMs - warningBeforeMaxInMs
left < right
}
def isMeetingInactive(nowInMs: Long): Boolean = {
@ -35,10 +37,14 @@ case class MeetingInactivityTracker(
case class MeetingExpiryTracker(
startedOnInMs: Long,
userHasJoined: Boolean,
isBreakout: Boolean,
lastUserLeftOnInMs: Option[Long],
durationInMs: Long,
meetingExpireIfNoUserJoinedInMs: Long,
meetingExpireWhenLastUserLeftInMs: Long
meetingExpireWhenLastUserLeftInMs: Long,
userInactivityInspectTimerInMs: Long,
userInactivityThresholdInMs: Long,
userActivitySignResponseDelayInMs: Long
) {
def setUserHasJoined(): MeetingExpiryTracker = {
if (!userHasJoined) {
@ -63,11 +69,11 @@ case class MeetingExpiryTracker(
}
def hasMeetingExpired(timestampInMs: Long): (Boolean, Option[String]) = {
if (hasMeetingExpiredNeverBeenJoined(timestampInMs)) {
if (hasMeetingExpiredNeverBeenJoined(timestampInMs) && !isBreakout) {
(true, Some(MeetingEndReason.ENDED_WHEN_NOT_JOINED))
} else if (meetingOverDuration(timestampInMs)) {
(true, Some(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION))
} else if (hasMeetingExpiredAfterLastUserLeft(timestampInMs)) {
} else if (hasMeetingExpiredAfterLastUserLeft(timestampInMs) && !isBreakout) {
(true, Some(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT))
} else {
(false, None)
@ -91,3 +97,31 @@ case class MeetingExpiryTracker(
}
}
case class MeetingRecordingTracker(
startedOnInMs: Long,
previousDurationInMs: Long,
currentDurationInMs: Long
) {
def startTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(startedOnInMs = nowInMs)
}
def pauseTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(currentDurationInMs = 0L, previousDurationInMs = previousDurationInMs + nowInMs - startedOnInMs, startedOnInMs = 0L)
}
def resetTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(startedOnInMs = nowInMs, previousDurationInMs = 0L, currentDurationInMs = 0L)
}
def udpateCurrentDuration(nowInMs: Long): MeetingRecordingTracker = {
copy(currentDurationInMs = nowInMs - startedOnInMs)
}
def recordingDuration(): Long = {
currentDurationInMs + previousDurationInMs
}
}

View File

@ -13,7 +13,8 @@ case class MeetingState2x(
presentationPodManager: PresentationPodManager,
breakout: Option[BreakoutModel],
inactivityTracker: MeetingInactivityTracker,
expiryTracker: MeetingExpiryTracker
expiryTracker: MeetingExpiryTracker,
recordingTracker: MeetingRecordingTracker
) {
def update(groupChats: GroupChats): MeetingState2x = copy(groupChats = groupChats)
@ -21,14 +22,16 @@ case class MeetingState2x(
def update(breakout: Option[BreakoutModel]): MeetingState2x = copy(breakout = breakout)
def update(expiry: MeetingExpiryTracker): MeetingState2x = copy(expiryTracker = expiry)
def update(inactivityTracker: MeetingInactivityTracker): MeetingState2x = copy(inactivityTracker = inactivityTracker)
def update(recordingTracker: MeetingRecordingTracker): MeetingState2x = copy(recordingTracker = recordingTracker)
}
object MeetingEndReason {
val ENDED_FROM_API = "ENDED_FROM_API"
val ENDED_DUE_TO_INACTIVITY = "ENDED_DUE_TO_ACTIVITY"
val ENDED_DUE_TO_INACTIVITY = "ENDED_DUE_TO_INACTIVITY"
val ENDED_WHEN_NOT_JOINED = "ENDED_WHEN_NOT_JOINED"
val ENDED_WHEN_LAST_USER_LEFT = "ENDED_WHEN_LAST_USER_LEFT"
val ENDED_AFTER_USER_LOGGED_OUT = "ENDED_AFTER_USER_LOGGED_OUT"
val ENDED_AFTER_EXCEEDING_DURATION = "ENDED_AFTER_EXCEEDING_DURATION"
val ENDED_BY_PARENT = "ENDED_BY_PARENT"
val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER"
}

View File

@ -9,9 +9,9 @@ object BreakoutRooms {
def breakoutRoomsdurationInMinutes(status: BreakoutRooms) = status.breakoutRoomsdurationInMinutes
def breakoutRoomsdurationInMinutes(status: BreakoutRooms, duration: Int) = status.breakoutRoomsdurationInMinutes = duration
def newBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, voiceConfId: String,
assignedUsers: Vector[String], breakoutRooms: BreakoutRooms): Option[BreakoutRoomVO] = {
val brvo = new BreakoutRoomVO(id, externalMeetingId, name, parentRoomId, sequence, voiceConfId, assignedUsers, Vector())
def newBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, freeJoin: Boolean,
voiceConfId: String, assignedUsers: Vector[String], breakoutRooms: BreakoutRooms): Option[BreakoutRoomVO] = {
val brvo = new BreakoutRoomVO(id, externalMeetingId, name, parentRoomId, sequence, freeJoin, voiceConfId, assignedUsers, Vector())
breakoutRooms.add(brvo)
Some(brvo)
}

View File

@ -20,6 +20,7 @@ case class GroupChats(chats: collection.immutable.Map[String, GroupChat]) {
def findAllPublicChats(): Vector[GroupChat] = chats.values.toVector filter (c => c.access == GroupChatAccess.PUBLIC)
def findAllPrivateChatsForUser(id: String) = chats.values.toVector filter (c =>
c.access == GroupChatAccess.PRIVATE && c.isUserMemberOf(id))
def getAllGroupChatsInMeeting(): Vector[GroupChat] = chats.values.toVector
}
case class GroupChat(id: String, name: String, access: String, createdBy: GroupChatUser,

View File

@ -56,6 +56,13 @@ object RegisteredUsers {
u
}
def updateUserRole(users: RegisteredUsers, user: RegisteredUser,
role: String): RegisteredUser = {
val u = user.modify(_.role).setTo(role)
users.save(u)
u
}
}
class RegisteredUsers {

View File

@ -1,6 +1,7 @@
package org.bigbluebutton.core.models
import com.softwaremill.quicklens._
import org.bigbluebutton.core.util.TimeUtil
object Users2x {
def findWithIntId(users: Users2x, intId: String): Option[UserState] = {
@ -26,6 +27,12 @@ object Users2x {
users.toVector.filter(u => !u.presenter)
}
def updateLastUserActivity(users: Users2x, u: UserState): UserState = {
val newUserState = modify(u)(_.lastActivityTime).setTo(TimeUtil.timeNowInMs())
users.save(newUserState)
newUserState
}
def changeRole(users: Users2x, u: UserState, newRole: String): UserState = {
val newUserState = modify(u)(_.role).setTo(newRole).modify(_.roleChangedOn).setTo(System.currentTimeMillis())
users.save(newUserState)
@ -103,6 +110,10 @@ object Users2x {
users.toVector.find(u => u.role == Roles.MODERATOR_ROLE)
}
def findAllAuthedUsers(users: Users2x): Vector[UserState] = {
users.toVector.find(u => u.authed).toVector
}
def addUserToPresenterGroup(users: Users2x, userIdToAdd: String): Boolean = {
users.updatePresenterGroup(users.presenterGroup.filterNot(_ == userIdToAdd).:+(userIdToAdd)) // ensure no repetition
users.presenterGroup.contains(userIdToAdd)
@ -202,9 +213,22 @@ class Users2x {
case class OldPresenter(userId: String, changedPresenterOn: Long)
case class UserState(intId: String, extId: String, name: String, role: String,
guest: Boolean, authed: Boolean, guestStatus: String, emoji: String, locked: Boolean,
presenter: Boolean, avatar: String, roleChangedOn: Long = System.currentTimeMillis())
case class UserState(
intId: String,
extId: String,
name: String,
role: String,
guest: Boolean,
authed: Boolean,
guestStatus: String,
emoji: String,
locked: Boolean,
presenter: Boolean,
avatar: String,
roleChangedOn: Long = System.currentTimeMillis(),
lastActivityTime: Long = TimeUtil.timeNowInMs(),
clientType: String
)
case class UserIdAndName(id: String, name: String)
@ -232,4 +256,5 @@ object EjectReasonCode {
val EJECT_USER = "user_requested_eject_reason"
val SYSTEM_EJECT_USER = "system_requested_eject_reason"
val VALIDATE_TOKEN = "validate_token_failed_eject_reason"
val USER_INACTIVITY = "user_inactivity_eject_reason"
}

View File

@ -94,6 +94,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[RemoveUserFromPresenterGroupCmdMsg](envelope, jsonNode)
case GetPresenterGroupReqMsg.NAME =>
routeGenericMsg[GetPresenterGroupReqMsg](envelope, jsonNode)
case UserActivitySignCmdMsg.NAME =>
routeGenericMsg[UserActivitySignCmdMsg](envelope, jsonNode)
// Poll
case StartCustomPollReqMsg.NAME =>
@ -184,7 +186,6 @@ class ReceivedJsonMsgHandlerActor(
case GetWhiteboardAnnotationsReqMsg.NAME =>
routeGenericMsg[GetWhiteboardAnnotationsReqMsg](envelope, jsonNode)
case ClientToServerLatencyTracerMsg.NAME =>
log.info("-- trace --" + jsonNode.toString)
routeGenericMsg[ClientToServerLatencyTracerMsg](envelope, jsonNode)
// Presentation
@ -254,6 +255,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[SendPrivateMessagePubMsg](envelope, jsonNode)
case ClearPublicChatHistoryPubMsg.NAME =>
routeGenericMsg[ClearPublicChatHistoryPubMsg](envelope, jsonNode)
case UserTypingPubMsg.NAME =>
routeGenericMsg[UserTypingPubMsg](envelope, jsonNode)
// Meeting
case EndMeetingSysCmdMsg.NAME =>
@ -264,6 +267,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[LogoutAndEndMeetingCmdMsg](envelope, jsonNode)
case SetRecordingStatusCmdMsg.NAME =>
routeGenericMsg[SetRecordingStatusCmdMsg](envelope, jsonNode)
case RecordAndClearPreviousMarkersCmdMsg.NAME =>
routeGenericMsg[RecordAndClearPreviousMarkersCmdMsg](envelope, jsonNode)
case GetRecordingStatusReqMsg.NAME =>
routeGenericMsg[GetRecordingStatusReqMsg](envelope, jsonNode)
case GetScreenshareStatusReqMsg.NAME =>

View File

@ -0,0 +1,39 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.core.record.events
class RecordStatusResetEvent extends AbstractParticipantRecordEvent {
import RecordStatusResetEvent._
setEvent("RecordStatusReset")
def setUserId(userId: String) {
eventMap.put(USER_ID, userId)
}
def setRecordingStatus(status: Boolean) {
eventMap.put(STATUS, status.toString)
}
}
object RecordStatusResetEvent {
protected final val USER_ID = "userId"
protected final val STATUS = "status"
}

View File

@ -9,6 +9,7 @@ import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models._
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, UserJoinedMeetingEvtMsgBuilder }
import org.bigbluebutton.core.util.TimeUtil
trait HandlerHelpers extends SystemConfiguration {
@ -25,7 +26,7 @@ trait HandlerHelpers extends SystemConfiguration {
outGW.send(event)
}
def userJoinMeeting(outGW: OutMsgRouter, authToken: String,
def userJoinMeeting(outGW: OutMsgRouter, authToken: String, clientType: String,
liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
val nu = for {
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers)
@ -41,7 +42,8 @@ trait HandlerHelpers extends SystemConfiguration {
emoji = "none",
presenter = false,
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
avatar = regUser.avatarURL
avatar = regUser.avatarURL,
clientType = clientType
)
}
@ -51,23 +53,36 @@ trait HandlerHelpers extends SystemConfiguration {
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser)
outGW.send(event)
startRecordingIfAutoStart2x(outGW, liveMeeting)
val newState = startRecordingIfAutoStart2x(outGW, liveMeeting, state)
if (!Users2x.hasPresenter(liveMeeting.users2x)) {
UsersApp.automaticallyAssignPresenter(outGW, liveMeeting)
}
state.update(state.expiryTracker.setUserHasJoined())
if (newUser.authed) {
if (!MeetingStatus2x.hasAuthedUserJoined(liveMeeting.status)) {
MeetingStatus2x.authUserHadJoined(liveMeeting.status)
}
if (MeetingStatus2x.getLastAuthedUserLeftOn(liveMeeting.status) > 0) {
MeetingStatus2x.resetLastAuthedUserLeftOn(liveMeeting.status)
}
}
newState.update(newState.expiryTracker.setUserHasJoined())
case None =>
state
}
}
def startRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting): Unit = {
def startRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
var newState = state
if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
MeetingStatus2x.recordingStarted(liveMeeting.status)
val tracker = state.recordingTracker.startTimer(TimeUtil.timeNowInMs())
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
@ -83,8 +98,38 @@ trait HandlerHelpers extends SystemConfiguration {
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
newState = state.update(tracker)
}
newState
}
def stopRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
var newState = state
if (liveMeeting.props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) {
MeetingStatus2x.recordingStopped(liveMeeting.status)
val tracker = state.recordingTracker.pauseTimer(TimeUtil.timeNowInMs())
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
newState = state.update(tracker)
}
newState
}
def endMeeting(outGW: OutMsgRouter, liveMeeting: LiveMeeting, reason: String): Unit = {

View File

@ -4,11 +4,12 @@ import java.io.{ PrintWriter, StringWriter }
import akka.actor._
import akka.actor.SupervisorStrategy.Resume
import org.bigbluebutton.core.apps.groupchats.{ GroupChatApp, GroupChatHdlrs }
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.apps.groupchats.GroupChatHdlrs
import org.bigbluebutton.core.apps.presentationpod._
import org.bigbluebutton.core.apps.users._
import org.bigbluebutton.core.apps.whiteboard.ClientToServerLatencyTracerMsgHdlr
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x }
import org.bigbluebutton.core.domain._
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.api._
@ -34,16 +35,14 @@ import scala.concurrent.duration._
import org.bigbluebutton.core.apps.layout.LayoutApp2x
import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr }
import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core2.testdata.FakeTestData
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
object MeetingActor {
def props(
props: DefaultProps,
eventBus: InternalEventBus,
outGW: OutMsgRouter,
liveMeeting: LiveMeeting
): Props =
liveMeeting: LiveMeeting): Props =
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
}
@ -51,9 +50,9 @@ class MeetingActor(
val props: DefaultProps,
val eventBus: InternalEventBus,
val outGW: OutMsgRouter,
val liveMeeting: LiveMeeting
)
val liveMeeting: LiveMeeting)
extends BaseMeetingActor
with SystemConfiguration
with GuestsApp
with LayoutApp2x
with VoiceApp2x
@ -78,7 +77,8 @@ class MeetingActor(
with ChangeLockSettingsInMeetingCmdMsgHdlr
with SyncGetMeetingInfoRespMsgHdlr
with ClientToServerLatencyTracerMsgHdlr
with ValidateConnAuthTokenSysMsgHdlr {
with ValidateConnAuthTokenSysMsgHdlr
with UserActivitySignCmdMsgHdlr {
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case e: Exception => {
@ -96,8 +96,7 @@ class MeetingActor(
*/
var actorMonitor = context.actorOf(
MeetingActorAudit.props(props, eventBus, outGW),
"actorMonitor-" + props.meetingProp.intId
)
"actorMonitor-" + props.meetingProp.intId)
val msgBus = MessageBus(eventBus, outGW)
@ -119,40 +118,45 @@ class MeetingActor(
TimeUtil.minutesToMillis(props.durationProps.warnMinutesBeforeMax),
lastActivityTimestampInMs = TimeUtil.timeNowInMs(),
warningSent = false,
warningSentOnTimestampInMs = 0L
)
warningSentOnTimestampInMs = 0L)
val expiryTracker = new MeetingExpiryTracker(
startedOnInMs = TimeUtil.timeNowInMs(),
userHasJoined = false,
isBreakout = props.meetingProp.isBreakout,
lastUserLeftOnInMs = None,
durationInMs = TimeUtil.minutesToMillis(props.durationProps.duration),
meetingExpireIfNoUserJoinedInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireIfNoUserJoinedInMinutes),
meetingExpireWhenLastUserLeftInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireWhenLastUserLeftInMinutes)
)
meetingExpireWhenLastUserLeftInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireWhenLastUserLeftInMinutes),
userInactivityInspectTimerInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityInspectTimerInMinutes),
userInactivityThresholdInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityInspectTimerInMinutes),
userActivitySignResponseDelayInMs = TimeUtil.minutesToMillis(props.durationProps.userActivitySignResponseDelayInMinutes))
val recordingTracker = new MeetingRecordingTracker(startedOnInMs = 0L, previousDurationInMs = 0L, currentDurationInMs = 0L)
var state = new MeetingState2x(
new GroupChats(Map.empty),
new PresentationPodManager(Map.empty),
None,
inactivityTracker,
expiryTracker
)
expiryTracker,
recordingTracker)
var lastRttTestSentOn = System.currentTimeMillis()
// Create a default publish group chat
state = GroupChatApp.createDefaultPublicGroupChat(GroupChatApp.MAIN_PUBLIC_CHAT, state)
//state = GroupChatApp.genTestChatMsgHistory(GroupChatApp.MAIN_PUBLIC_CHAT, state, BbbSystemConst.SYSTEM_USER, liveMeeting)
// Create a default public group chat
state = groupChatApp.handleCreateDefaultPublicGroupChat(state, liveMeeting, msgBus)
// Create a default publish group chat
//state = GroupChatApp.genTestChatMsgHistory(GroupChatApp.MAIN_PUBLIC_CHAT, state, BbbSystemConst.SYSTEM_USER, liveMeeting)
// Create a default public group chat **DEPRECATED, NOT GOING TO WORK ANYMORE**
//state = GroupChatApp.createDefaultPublicGroupChat("TEST_GROUP_CHAT", state)
//state = GroupChatApp.genTestChatMsgHistory("TEST_GROUP_CHAT", state, BbbSystemConst.SYSTEM_USER, liveMeeting)
log.debug("NUM GROUP CHATS = " + state.groupChats.findAllPublicChats().length)
// Create a default Presentation Pod
state = PresentationPodsApp.createDefaultPresentationPod(state)
state = presentationPodsApp.handleCreateDefaultPresentationPod(state, liveMeeting, msgBus)
log.debug("NUM Presentation Pods = " + state.presentationPodManager.getNumberOfPods())
// Initialize if the meeting is muted on start
@ -206,12 +210,34 @@ class MeetingActor(
// Screenshare
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
case msg: SendRecordingTimerInternalMsg =>
state = usersApp.handleSendRecordingTimerInternalMsg(msg, state)
case _ => // do nothing
}
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
private def updateInactivityTracker(state: MeetingState2x): MeetingState2x = {
val tracker = state.inactivityTracker.updateLastActivityTimestamp(TimeUtil.timeNowInMs())
state = state.update(tracker)
state.update(tracker)
}
private def updateVoiceUserLastActivity(userId: String) {
for {
vu <- VoiceUsers.findWithVoiceUserId(liveMeeting.voiceUsers, userId)
} yield {
updateUserLastActivity(vu.intId)
}
}
private def updateUserLastActivity(userId: String) {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
} yield {
Users2x.updateLastUserActivity(liveMeeting.users2x, user)
}
}
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
msg.core match {
case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state)
@ -224,19 +250,32 @@ class MeetingActor(
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
case m: MeetingActivityResponseCmdMsg => state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
case m: MeetingActivityResponseCmdMsg =>
state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
state = updateInactivityTracker(state)
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m)
case m: SetRecordingStatusCmdMsg =>
state = usersApp.handleSetRecordingStatusCmdMsg(m, state)
updateUserLastActivity(m.body.setBy)
case m: RecordAndClearPreviousMarkersCmdMsg =>
state = usersApp.handleRecordAndClearPreviousMarkersCmdMsg(m, state)
updateUserLastActivity(m.body.setBy)
case m: GetWebcamsOnlyForModeratorReqMsg => usersApp.handleGetWebcamsOnlyForModeratorReqMsg(m)
case m: UpdateWebcamsOnlyForModeratorCmdMsg => usersApp.handleUpdateWebcamsOnlyForModeratorCmdMsg(m)
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
// Client requested to eject user
case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m)
case m: EjectUserFromMeetingCmdMsg =>
usersApp.handleEjectUserFromMeetingCmdMsg(m)
updateUserLastActivity(m.body.ejectedBy)
// Another part of system (e.g. bbb-apps) requested to eject user.
case m: EjectUserFromMeetingSysMsg => usersApp.handleEjectUserFromMeetingSysMsg(m)
case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m)
case m: ChangeUserRoleCmdMsg => usersApp.handleChangeUserRoleCmdMsg(m)
case m: ChangeUserRoleCmdMsg =>
usersApp.handleChangeUserRoleCmdMsg(m)
updateUserLastActivity(m.body.changedBy)
// Whiteboard
case m: SendCursorPositionPubMsg => wbApp.handle(m, liveMeeting, msgBus)
@ -249,12 +288,22 @@ class MeetingActor(
case m: ClientToServerLatencyTracerMsg => handleClientToServerLatencyTracerMsg(m)
// Poll
case m: StartPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
case m: StartCustomPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
case m: StopPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
case m: ShowPollResultReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
case m: StartPollReqMsg =>
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
updateUserLastActivity(m.body.requesterId)
case m: StartCustomPollReqMsg =>
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
updateUserLastActivity(m.body.requesterId)
case m: StopPollReqMsg =>
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
updateUserLastActivity(m.body.requesterId)
case m: ShowPollResultReqMsg =>
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
updateUserLastActivity(m.body.requesterId)
case m: GetCurrentPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
case m: RespondToPollReqMsg => pollApp.handle(m, liveMeeting, msgBus)
case m: RespondToPollReqMsg =>
pollApp.handle(m, liveMeeting, msgBus)
updateUserLastActivity(m.body.requesterId)
// Breakout
case m: BreakoutRoomsListMsg => state = handleBreakoutRoomsListMsg(m, state)
@ -266,13 +315,23 @@ class MeetingActor(
// Voice
case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m)
case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m)
case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m)
case m: UserTalkingInVoiceConfEvtMsg =>
state = updateInactivityTracker(state)
updateVoiceUserLastActivity(m.body.voiceUserId)
handleUserTalkingInVoiceConfEvtMsg(m)
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
case m: MuteUserCmdMsg => usersApp.handleMuteUserCmdMsg(m)
case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m)
case m: MuteUserCmdMsg =>
usersApp.handleMuteUserCmdMsg(m)
updateUserLastActivity(m.body.mutedBy)
case m: MuteAllExceptPresentersCmdMsg =>
handleMuteAllExceptPresentersCmdMsg(m)
updateUserLastActivity(m.body.mutedBy)
case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m)
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m)
case m: MuteMeetingCmdMsg =>
handleMuteMeetingCmdMsg(m)
updateUserLastActivity(m.body.mutedBy)
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m)
case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m)
case m: VoiceConfRunningEvtMsg => handleVoiceConfRunningEvtMsg(m)
@ -282,7 +341,9 @@ class MeetingActor(
case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m)
// Lock Settings
case m: ChangeLockSettingsInMeetingCmdMsg => handleSetLockSettings(m)
case m: ChangeLockSettingsInMeetingCmdMsg =>
handleSetLockSettings(m)
updateUserLastActivity(m.body.setBy)
case m: LockUserInMeetingCmdMsg => handleLockUserInMeetingCmdMsg(m)
case m: LockUsersInMeetingCmdMsg => handleLockUsersInMeetingCmdMsg(m)
case m: GetLockSettingsReqMsg => handleGetLockSettingsReqMsg(m)
@ -328,9 +389,14 @@ class MeetingActor(
// Chat
case m: GetChatHistoryReqMsg => chatApp2x.handle(m, liveMeeting, msgBus)
case m: SendPublicMessagePubMsg => chatApp2x.handle(m, liveMeeting, msgBus)
case m: SendPrivateMessagePubMsg => chatApp2x.handle(m, liveMeeting, msgBus)
case m: SendPublicMessagePubMsg =>
chatApp2x.handle(m, liveMeeting, msgBus)
updateUserLastActivity(m.body.message.fromUserId)
case m: SendPrivateMessagePubMsg =>
chatApp2x.handle(m, liveMeeting, msgBus)
updateUserLastActivity(m.body.message.fromUserId)
case m: ClearPublicChatHistoryPubMsg => state = chatApp2x.handle(m, state, liveMeeting, msgBus)
case m: UserTypingPubMsg => chatApp2x.handle(m, liveMeeting, msgBus)
// Screenshare
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
@ -340,13 +406,19 @@ class MeetingActor(
case m: GetScreenshareStatusReqMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
// GroupChat
case m: CreateGroupChatReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus)
case m: CreateGroupChatReqMsg =>
state = groupChatApp.handle(m, state, liveMeeting, msgBus)
updateUserLastActivity(m.header.userId)
case m: GetGroupChatMsgsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus)
case m: GetGroupChatsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus)
case m: SendGroupChatMessageMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus)
case m: SendGroupChatMessageMsg =>
state = groupChatApp.handle(m, state, liveMeeting, msgBus)
updateUserLastActivity(m.body.msg.sender.id)
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
case m: UserActivitySignCmdMsg => handleUserActivitySignCmdMsg(m)
case _ => log.warning("***** Cannot handle " + msg.envelope.name)
}
}
@ -361,9 +433,15 @@ class MeetingActor(
// sync all presentations
presentationPodsApp.handleSyncGetPresentationPods(state, liveMeeting, msgBus)
// TODO send all chat
// sync all group chats and group chat messages
groupChatApp.handleSyncGetGroupChatsInfo(state, liveMeeting, msgBus)
// sync all voice users
handleSyncGetVoiceUsersMsg(state, liveMeeting, msgBus)
// TODO send all lock settings
// TODO send all screen sharing info
}
def handlePresenterChange(msg: AssignPresenterReqMsg, state: MeetingState2x): MeetingState2x = {
@ -377,8 +455,7 @@ class MeetingActor(
screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(
liveMeeting.props.voiceProp.voiceConf,
liveMeeting.props.screenshareProps.screenshareConf,
liveMeeting, msgBus
)
liveMeeting, msgBus)
newState
@ -409,6 +486,10 @@ class MeetingActor(
sendRttTraceTest()
setRecordingChapterBreak()
processUserInactivityAudit()
checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting)
}
var lastRecBreakSentOn = expiryTracker.startedOnInMs
@ -452,6 +533,25 @@ class MeetingActor(
}
private def checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting: LiveMeeting): Unit = {
val authUserJoined = MeetingStatus2x.hasAuthedUserJoined(liveMeeting.status)
if (endMeetingWhenNoMoreAuthedUsers &&
!liveMeeting.props.meetingProp.isBreakout &&
authUserJoined) {
val lastAuthedUserLeftLimitMs = TimeUtil.timeNowInMs() - MeetingStatus2x.getLastAuthedUserLeftOn(liveMeeting.status)
if (lastAuthedUserLeftLimitMs > TimeUtil.minutesToMillis(endMeetingWhenNoMoreAuthedUsersAfterMinutes)) {
val authedUsers = Users2x.findAllAuthedUsers(liveMeeting.users2x)
if (authedUsers.isEmpty) {
sendEndMeetingDueToExpiry(
MeetingEndReason.ENDED_DUE_TO_NO_AUTHED_USER,
eventBus, outGW, liveMeeting)
}
}
}
}
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
}
@ -474,8 +574,7 @@ class MeetingActor(
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
"system", MeetingStatus2x.isRecording(liveMeeting.status))
outGW.send(event)
}
@ -499,10 +598,50 @@ class MeetingActor(
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
"system", MeetingStatus2x.isRecording(liveMeeting.status))
outGW.send(event)
}
}
var lastUserInactivityInspectSentOn = TimeUtil.timeNowInMs()
var checkInactiveUsers = false
def processUserInactivityAudit(): Unit = {
val now = TimeUtil.timeNowInMs()
// Time to do a new check?
if (now > lastUserInactivityInspectSentOn + expiryTracker.userInactivityInspectTimerInMs) {
lastUserInactivityInspectSentOn = now
checkInactiveUsers = true
warnPotentiallyInactiveUsers()
}
if (checkInactiveUsers && now > lastUserInactivityInspectSentOn + expiryTracker.userActivitySignResponseDelayInMs) {
checkInactiveUsers = false
disconnectInactiveUsers()
}
}
def warnPotentiallyInactiveUsers(): Unit = {
log.info("Checking for inactive users.")
val users = Users2x.findAll(liveMeeting.users2x)
users foreach { u =>
val active = (lastUserInactivityInspectSentOn - expiryTracker.userInactivityThresholdInMs) < u.lastActivityTime
if (!active) {
Sender.sendUserInactivityInspectMsg(liveMeeting.props.meetingProp.intId, u.intId, TimeUtil.minutesToSeconds(props.durationProps.userActivitySignResponseDelayInMinutes), outGW)
}
}
}
def disconnectInactiveUsers(): Unit = {
log.info("Check for users who haven't responded to user inactivity warning.")
val users = Users2x.findAll(liveMeeting.users2x)
users foreach { u =>
val respondedOntIme = (lastUserInactivityInspectSentOn - expiryTracker.userInactivityThresholdInMs) < u.lastActivityTime && (lastUserInactivityInspectSentOn + expiryTracker.userActivitySignResponseDelayInMs) > u.lastActivityTime
if (!respondedOntIme) {
UsersApp.ejectUserFromMeeting(outGW, liveMeeting, u.intId, SystemUser.ID, "User inactive for too long.", EjectReasonCode.USER_INACTIVITY)
Sender.sendDisconnectClientSysMsg(liveMeeting.props.meetingProp.intId, u.intId, SystemUser.ID, EjectReasonCode.USER_INACTIVITY, outGW)
}
}
}
}
}
}

View File

@ -80,6 +80,11 @@ class MeetingActorAudit(
props.meetingProp.intId,
SendBreakoutUsersAuditInternalMsg(props.breakoutProps.parentId, props.meetingProp.intId)
))
// Trigger recording timer, only for meeting allowing recording
if (props.recordProp.record) {
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendRecordingTimerInternalMsg(props.meetingProp.intId)))
}
}
}

View File

@ -74,8 +74,18 @@ class AnalyticsActor extends Actor with ActorLogging {
case m: MeetingInactivityWarningEvtMsg => logMessage(msg)
case m: StartRecordingVoiceConfSysMsg => logMessage(msg)
case m: StopRecordingVoiceConfSysMsg => logMessage(msg)
//case m: UpdateRecordingTimerEvtMsg => logMessage(msg)
case m: RecordAndClearPreviousMarkersCmdMsg => logMessage(msg)
case m: TransferUserToVoiceConfSysMsg => logMessage(msg)
case m: UserBroadcastCamStartMsg => logMessage(msg)
case m: UserBroadcastCamStopMsg => logMessage(msg)
case m: UserBroadcastCamStoppedEvtMsg => logMessage(msg)
case m: UserBroadcastCamStartedEvtMsg => logMessage(msg)
case m: EjectUserFromMeetingSysMsg => logMessage(msg)
case m: UserActivitySignCmdMsg => logMessage(msg)
case m: UserInactivityInspectMsg => logMessage(msg)
case m: ChangeUserRoleCmdMsg => logMessage(msg)
// Breakout
case m: BreakoutRoomEndedEvtMsg => logMessage(msg)

View File

@ -25,6 +25,9 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
case SyncGetPresentationPodsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetMeetingInfoRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetUsersMeetingRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetGroupChatsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetGroupChatMsgsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetVoiceUsersRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
// Sent to FreeSWITCH
case ScreenshareStartRtmpBroadcastVoiceConfMsg.NAME =>
@ -75,8 +78,6 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
msgSender.send(fromAkkaAppsPresRedisChannel, json)
case RemovePresentationEvtMsg.NAME =>
msgSender.send(fromAkkaAppsPresRedisChannel, json)
case SetPresentationDownloadableEvtMsg.NAME =>
msgSender.send(fromAkkaAppsPresRedisChannel, json)
case SetCurrentPresentationEvtMsg.NAME =>
msgSender.send(fromAkkaAppsPresRedisChannel, json)

View File

@ -2,6 +2,8 @@ package org.bigbluebutton.core2
import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.util.TimeUtil
case class Permissions(
disableCam: Boolean = false,
disableMic: Boolean = false,
@ -45,6 +47,13 @@ object MeetingStatus2x {
def recordingStopped(status: MeetingStatus2x) = status.recording = false
def isRecording(status: MeetingStatus2x): Boolean = status.recording
def authUserHadJoined(status: MeetingStatus2x) = status.authedUserHasJoined = true
def hasAuthedUserJoined(status: MeetingStatus2x): Boolean = status.authedUserHasJoined
def setLastAuthedUserLeftOn(status: MeetingStatus2x) = status.lastAuthedUserLeftOn = TimeUtil.timeNowInMs()
def getLastAuthedUserLeftOn(status: MeetingStatus2x): Long = status.lastAuthedUserLeftOn
def resetLastAuthedUserLeftOn(status: MeetingStatus2x) = status.lastAuthedUserLeftOn = 0L
def voiceRecordingStart(status2x: MeetingStatus2x, stream: String): VoiceRecordingStream = {
val vrs = new VoiceRecordingStream(stream, recording = false, createdOn = System.currentTimeMillis, ackedOn = None, stoppedOn = None)
status2x.voiceRecordings += vrs.stream -> vrs
@ -115,6 +124,8 @@ class MeetingStatus2x {
private var webcamsOnlyForModerator = false
private var authedUserHasJoined = false
private var lastAuthedUserLeftOn = 0L
}
case class VoiceRecordingStream(stream: String, recording: Boolean, createdOn: Long, ackedOn: Option[Long], stoppedOn: Option[Long])

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ SendTimeRemainingAuditInternalMsg }
import org.bigbluebutton.core.api.SendTimeRemainingAuditInternalMsg
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil

View File

@ -156,6 +156,16 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildRecordStatusResetSysMsg(meetingId: String, recording: Boolean, setBy: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.SYSTEM, meetingId, setBy)
val envelope = BbbCoreEnvelope(RecordStatusResetSysMsg.NAME, routing)
val body = RecordStatusResetSysMsgBody(recording, setBy)
val header = BbbCoreHeaderWithMeetingId(RecordStatusResetSysMsg.NAME, meetingId)
val event = RecordStatusResetSysMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildDisconnectAllClientsSysMsg(meetingId: String, reason: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.SYSTEM, meetingId, "not-used")
val envelope = BbbCoreEnvelope(DisconnectAllClientsSysMsg.NAME, routing)
@ -208,6 +218,16 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildUserInactivityInspectMsg(meetingId: String, userId: String, responseDelay: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(UserInactivityInspectMsg.NAME, routing)
val body = UserInactivityInspectMsgBody(meetingId, responseDelay)
val header = BbbClientMsgHeader(UserInactivityInspectMsg.NAME, meetingId, userId)
val event = UserInactivityInspectMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildCheckAlivePingSysMsg(system: String, timestamp: Long): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(CheckAlivePongSysMsg.NAME, routing)
@ -286,4 +306,15 @@ object MsgBuilder {
val event = StopRecordingVoiceConfSysMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildCreateNewPresentationPodEvtMsg(meetingId: String, currentPresenterId: String, podId: String, userId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(CreateNewPresentationPodEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(CreateNewPresentationPodEvtMsg.NAME, meetingId, userId)
val body = CreateNewPresentationPodEvtMsgBody(currentPresenterId, podId)
val event = CreateNewPresentationPodEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}

View File

@ -7,17 +7,19 @@ object Sender {
def sendUserEjectedFromMeetingClientEvtMsg(meetingId: String, userId: String,
ejectedBy: String, reason: String,
reasonCode: String, outGW: OutMsgRouter): Unit = {
val ejectFromMeetingClientEvent = MsgBuilder.buildUserEjectedFromMeetingEvtMsg(
meetingId, userId, ejectedBy, reason, reasonCode
)
val ejectFromMeetingClientEvent = MsgBuilder.buildUserEjectedFromMeetingEvtMsg(meetingId, userId, ejectedBy, reason, reasonCode)
outGW.send(ejectFromMeetingClientEvent)
}
def sendDisconnectClientSysMsg(meetingId: String, userId: String,
ejectedBy: String, reason: String, outGW: OutMsgRouter): Unit = {
val ejectFromMeetingSystemEvent = MsgBuilder.buildDisconnectClientSysMsg(
meetingId, userId, ejectedBy, reason
)
val ejectFromMeetingSystemEvent = MsgBuilder.buildDisconnectClientSysMsg(meetingId, userId, ejectedBy, reason)
outGW.send(ejectFromMeetingSystemEvent)
}
def sendUserInactivityInspectMsg(meetingId: String, userId: String, responseDelay: Long, outGW: OutMsgRouter): Unit = {
val userInactivityInspectMsg = MsgBuilder.buildUserInactivityInspectMsg(meetingId, userId, responseDelay)
outGW.send(userInactivityInspectMsg)
}
}

View File

@ -10,8 +10,10 @@ object UserJoinedMeetingEvtMsgBuilder {
val body = UserJoinedMeetingEvtMsgBody(intId = userState.intId, extId = userState.extId, name = userState.name,
role = userState.role, guest = userState.guest, authed = userState.authed,
guestStatus = userState.guestStatus, emoji = userState.emoji,
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar)
guestStatus = userState.guestStatus,
emoji = userState.emoji,
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar,
clientType = userState.clientType)
val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body)

View File

@ -68,7 +68,6 @@ trait FakeTestData {
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL)
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, clientType = "unknown")
}
}

View File

@ -95,6 +95,7 @@ class RedisRecorderActor(val system: ActorSystem)
// Meeting
case m: RecordingStatusChangedEvtMsg => handleRecordingStatusChangedEvtMsg(m)
case m: RecordStatusResetSysMsg => handleRecordStatusResetSysMsg(m)
case m: WebcamsOnlyForModeratorChangedEvtMsg => handleWebcamsOnlyForModeratorChangedEvtMsg(m)
case m: EndAndKickAllSysMsg => handleEndAndKickAllSysMsg(m)
@ -464,6 +465,15 @@ class RedisRecorderActor(val system: ActorSystem)
record(msg.header.meetingId, ev.toMap)
}
private def handleRecordStatusResetSysMsg(msg: RecordStatusResetSysMsg) {
val ev = new RecordStatusResetEvent()
ev.setMeetingId(msg.header.meetingId)
ev.setUserId(msg.body.setBy)
ev.setRecordingStatus(msg.body.recording)
record(msg.header.meetingId, ev.toMap)
}
private def handleWebcamsOnlyForModeratorChangedEvtMsg(msg: WebcamsOnlyForModeratorChangedEvtMsg) {
val ev = new WebcamsOnlyForModeratorRecordEvent()
ev.setMeetingId(msg.header.meetingId)

26
akka-bbb-fsesl/Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM bbb-fsesl-client AS builder
ARG COMMON_VERSION=0.0.1-SNAPSHOT
COPY . /source
RUN cd /source \
&& find -name build.sbt -exec sed -i "s|\(.*org.bigbluebutton.*bbb-common-message[^\"]*\"[ ]*%[ ]*\)\"[^\"]*\"\(.*\)|\1\"$COMMON_VERSION\"\2|g" {} \; \
&& find -name build.sbt -exec sed -i "s|\(.*org.bigbluebutton.*bbb-fsesl-client[^\"]*\"[ ]*%[ ]*\)\"[^\"]*\"\(.*\)|\1\"$COMMON_VERSION\"\2|g" {} \; \
&& sbt compile
RUN apt-get update \
&& apt-get -y install fakeroot
RUN cd /source \
&& sbt debian:packageBin
FROM openjdk:8-jre-slim-stretch
COPY --from=builder /source/target/*.deb /root/
RUN dpkg -i /root/*.deb
COPY wait-for-it.sh /usr/local/bin/
CMD ["/usr/share/bbb-fsesl-akka/bin/bbb-fsesl-akka"]

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
</encoder>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/bbb-fsesl-akka.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/bbb-fsesl-akka.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 30 days worth of history -->
<MaxHistory>5</MaxHistory>
<!-- keep 14 days worth of history -->
<MaxHistory>14</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd HH:mm:ss,SSSXXX"} [%thread] %-5level %logger{35} - %msg%n</Pattern>
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>

177
akka-bbb-fsesl/wait-for-it.sh Executable file
View File

@ -0,0 +1,177 @@
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
cmdname=$(basename $0)
echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $TIMEOUT -gt 0 ]]; then
echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT"
else
echoerr "$cmdname: waiting for $HOST:$PORT without a timeout"
fi
start_ts=$(date +%s)
while :
do
if [[ $ISBUSY -eq 1 ]]; then
nc -z $HOST $PORT
result=$?
else
(echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1
result=$?
fi
if [[ $result -eq 0 ]]; then
end_ts=$(date +%s)
echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds"
break
fi
sleep 1
done
return $result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $QUIET -eq 1 ]]; then
timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
else
timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
fi
PID=$!
trap "kill -INT -$PID" INT
wait $PID
RESULT=$?
if [[ $RESULT -ne 0 ]]; then
echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT"
fi
return $RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
hostport=(${1//:/ })
HOST=${hostport[0]}
PORT=${hostport[1]}
shift 1
;;
--child)
CHILD=1
shift 1
;;
-q | --quiet)
QUIET=1
shift 1
;;
-s | --strict)
STRICT=1
shift 1
;;
-h)
HOST="$2"
if [[ $HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
HOST="${1#*=}"
shift 1
;;
-p)
PORT="$2"
if [[ $PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
PORT="${1#*=}"
shift 1
;;
-t)
TIMEOUT="$2"
if [[ $TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$HOST" == "" || "$PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
TIMEOUT=${TIMEOUT:-15}
STRICT=${STRICT:-0}
CHILD=${CHILD:-0}
QUIET=${QUIET:-0}
# check to see if timeout is from busybox?
# check to see if timeout is from busybox?
TIMEOUT_PATH=$(realpath $(which timeout))
if [[ $TIMEOUT_PATH =~ "busybox" ]]; then
ISBUSY=1
BUSYTIMEFLAG="-t"
else
ISBUSY=0
BUSYTIMEFLAG=""
fi
if [[ $CHILD -gt 0 ]]; then
wait_for
RESULT=$?
exit $RESULT
else
if [[ $TIMEOUT -gt 0 ]]; then
wait_for_wrapper
RESULT=$?
else
wait_for
RESULT=$?
fi
fi
if [[ $CLI != "" ]]; then
if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then
echoerr "$cmdname: strict mode, refusing to execute subprocess"
exit $RESULT
fi
exec "${CLI[@]}"
else
exit $RESULT
fi

View File

@ -303,7 +303,7 @@ public class VideoTranscoder extends UntypedActor implements ProcessMonitorObser
ffmpeg = new FFmpegCommand();
ffmpeg.setFFmpegPath(FFMPEG_PATH);
ffmpeg.setInput(input);
ffmpeg.setProtocolWhitelist("file,udp,rtp");
ffmpeg.setLoglevel("quiet");
ffmpeg.setOutput(outputLive);
ffmpeg.addRtmpOutputConnectionParameter(meetingId);

View File

@ -29,6 +29,8 @@ public class FFmpegCommand {
private int frameRate;
private String frameSize;
private String protocolWhitelist;
public FFmpegCommand() {
this.args = new HashMap();
this.x264Params = new HashMap();
@ -82,6 +84,11 @@ public class FFmpegCommand {
comm.add(probeSize);
}
if(protocolWhitelist != null && !protocolWhitelist.isEmpty()) {
comm.add("-protocol_whitelist");
comm.add(protocolWhitelist);
}
buildRtmpInput();
comm.add("-i");
@ -323,6 +330,14 @@ public class FFmpegCommand {
this.frameSize = value;
}
/**
* Sets protocol elements to be whitelisted
* @param whitelist
*/
public void setProtocolWhitelist(String whitelist) {
this.protocolWhitelist = whitelist;
}
/**
* Add parameters for rtmp connections.
* The order of parameters is the order they are added

View File

@ -4,6 +4,7 @@ akka {
receive = on
}
}
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = INFO
stdout-loglevel = "INFO"

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
</encoder>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/bbb-transcode.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/bbb-transcode.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 30 days worth of history -->
<MaxHistory>5</MaxHistory>
<!-- keep 14 days worth of history -->
<MaxHistory>14</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd HH:mm:ss,SSSXXX"} [%thread] %-5level %logger{35} - %msg%n</Pattern>
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout>
</appender>

View File

@ -5,7 +5,7 @@ import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import akka.actor.ActorSystem
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common.converters.ToJsonEncoder
//import org.bigbluebutton.common.converters.ToJsonEncoder
class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
@ -15,13 +15,13 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
// CLIENT LIST on redis-cli
redis.clientSetname("BbbTranscodeAkkaPub")
val encoder = new ToJsonEncoder()
def sendPingMessage() {
val json = encoder.encodePubSubPingMessage("BbbTranscode", System.currentTimeMillis())
redis.publish("bigbluebutton:to-bbb-apps:system", json)
}
//val encoder = new ToJsonEncoder()
//def sendPingMessage() {
// val json = encoder.encodePubSubPingMessage("BbbTranscode", System.currentTimeMillis())
// redis.publish("bigbluebutton:to-bbb-apps:system", json)
//}
system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage())
//system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage())
def publish(channel: String, data: String) {
//println("PUBLISH TO [" + channel + "]: \n [" + data + "]")

View File

@ -150,8 +150,6 @@ public String getJoinMeetingURL(String username, String meetingID, String passwo
}
//
// Create a meeting and return a URL to join it as moderator. This is used for the API demos.
//
@ -169,6 +167,32 @@ public String getJoinMeetingURL(String username, String meetingID, String passwo
// Note this meeting will use username for meetingID
public String getJoinURL(String username, String meetingID, String record, String welcome, Map<String, String> metadata, String xml) {
String isHTML5Client = "false";
String isModerator = "true";
return getJoinURLExtended(username, meetingID, record, welcome, metadata, xml, isHTML5Client, isModerator);
}
//
// Create a meeting and return a URL to join it as moderator. This is used for the API demos.
//
// Passed
// - username
// - meetingID
// - record ["true", "false"]
// - welcome message (null causes BigBlueButton to use the default welcome message
// - metadata (passed through when record="true"
// - xml (used for pre-upload of slides)_
// - isHTML5Client ["true", "false"]
// - isModerator ["true", "false"]
//
// Returned
// - valid join URL using the username
//
// Note this meeting will use username for meetingID
public String getJoinURLExtended(String username, String meetingID, String record, String welcome, Map<String, String> metadata, String xml, String isHTML5Client, String isModerator) {
String base_url_create = BigBlueButtonURL + "api/create?";
String base_url_join = BigBlueButtonURL + "api/join?";
@ -199,7 +223,6 @@ public String getJoinURL(String username, String meetingID, String record, Strin
//
// Note: We're hard-coding the password for moderator and attendee (viewer) for purposes of demo.
//
String create_parameters = "name=" + urlEncode(meetingID)
+ "&meetingID=" + urlEncode(meetingID) + welcome_param + voiceBridge_param
+ "&attendeePW=ap&moderatorPW=mp"
@ -207,6 +230,11 @@ public String getJoinURL(String username, String meetingID, String record, Strin
+ "&record=" + record + getMetaData( metadata );
String password = "mp"; // Attempt to join as moderator by default
if ("false" == isModerator) {
password = "ap"; // default attendee password
}
// Attempt to create a meeting using meetingID
Document doc = null;
try {
@ -227,7 +255,10 @@ public String getJoinURL(String username, String meetingID, String record, Strin
//
String join_parameters = "meetingID=" + urlEncode(meetingID)
+ "&fullName=" + urlEncode(username) + "&password=mp";
+ "&fullName=" + urlEncode(username)
+ "&joinViaHtml5=" + isHTML5Client
+ "&password=" + password;
return base_url_join + join_parameters + "&checksum="
+ checksum("join" + join_parameters + salt);
@ -238,6 +269,7 @@ public String getJoinURL(String username, String meetingID, String record, Strin
+ ": "
+ doc.getElementsByTagName("message").item(0).getTextContent()
.trim();
}
@ -967,3 +999,4 @@ public static Element getElementWithAttribute(Node root, String attrName, String
}
%>

View File

@ -70,7 +70,7 @@ if (request.getParameterMap().isEmpty()) {
<td>&nbsp;</td>
<td style="text-align: right; ">Moderator Role:</td>
<td style="width: 5px; ">&nbsp;</td>
<td style="text-align: left "><input type=checkbox name=isModerator value="true"></td>
<td style="text-align: left "><input type=checkbox name=isModerator value="true" checked></td>
<tr>
<tr>
@ -88,10 +88,6 @@ if (request.getParameterMap().isEmpty()) {
<%
} else if (request.getParameter("action").equals("create")) {
//
// Got an action=create
//
String username = request.getParameter("username");
// set defaults and overwrite them if custom values exist
@ -100,27 +96,14 @@ if (request.getParameterMap().isEmpty()) {
meetingname = request.getParameter("meetingname");
}
String defaultModeratorPassword = "mp";
String defaultAttendeePassword = "ap";
String defaultPassword = defaultAttendeePassword;
boolean isModerator = false;
Boolean isModerator = new Boolean(false);
Boolean isHTML5 = new Boolean(true);
Boolean isRecorded = new Boolean(true);
if (request.getParameter("isModerator") != null) {
isModerator = Boolean.parseBoolean(request.getParameter("isModerator"));
defaultPassword = defaultModeratorPassword;
}
String ip = BigBlueButtonURL.split("\\/bigbluebutton")[0];
String html5url = ip + "/html5client/join";
String meetingId = createMeeting( meetingname, null, defaultModeratorPassword, "Welcome moderator! (moderator only message)", defaultAttendeePassword, null, null );
// Check if we have an existing meeting
if( meetingId.startsWith("Error ")) {
meetingId = meetingname;
}
String joinURL = getJoinMeetingURL(username, meetingId, defaultPassword, html5url);
String joinURL = getJoinURLExtended(username, meetingname, isRecorded.toString(), null, null, null, isHTML5.toString(), isModerator.toString());
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>

View File

@ -0,0 +1,115 @@
<!--
BigBlueButton - http://www.bigbluebutton.org
Copyright (c) 2008-2018 by respective authors (see below). All rights reserved.
BigBlueButton is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, If not, see <http://www.gnu.org/licenses/>.
-->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Join Video Chat</title>
</head>
<body>
<p>You must have the BigBlueButton HTML5 client installed to use this API demo.</p>
<%@ include file="bbb_api.jsp"%>
<%
if (request.getParameterMap().isEmpty()) {
//
// Assume we want to create a meeting
//
%>
<%@ include file="demo_header.jsp"%>
<h2>Video Chat via HTML5 Client</h2>
<FORM NAME="form1" METHOD="GET">
<table cellpadding="5" cellspacing="5" style="width: 400px; ">
<tbody>
<tr>
<td>&nbsp;</td>
<td style="text-align: right; ">Full Name:</td>
<td style="width: 5px; ">&nbsp;</td>
<td style="text-align: left "><input type="text" autofocus required name="username" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><input type="submit" value="Join" /></td>
<tr>
</tbody>
</table>
<INPUT TYPE=hidden NAME=action VALUE="create">
</FORM>
<%
} else if (request.getParameter("action").equals("create")) {
String username = request.getParameter("username");
String meetingname = "Video Chat Meeting";
//metadata
Map<String,String> metadata=new HashMap<String,String>();
metadata.put("html5autoswaplayout", "true");
metadata.put("html5autosharewebcam", "true");
metadata.put("html5hidepresentation", "true");
Boolean isModerator = new Boolean(true);
Boolean isHTML5 = new Boolean(true);
Boolean isRecorded = new Boolean(true);
String joinURL = getJoinURLExtended(username, meetingname, isRecorded.toString(), null, metadata, null, isHTML5.toString(), isModerator.toString());
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">
window.location.href="<%=joinURL%>";
</script>
<%
} else {
%>
Error: getJoinURL() failed
<p/>
<%=joinURL %>
<%
}
}
%>
<%@ include file="demo_footer.jsp"%>
</body>
</html>

View File

@ -20,3 +20,5 @@
<a href="demoHTML5.jsp">HTML5 Client Demo</a> &nbsp;&nbsp;
<a href="demoHTML5Video.jsp">HTML5 Client Video Chat</a> &nbsp;&nbsp;

View File

@ -101,7 +101,7 @@ You must have the BigBlueButton mobile client installed on your device for this
<%
} else if (joinURL.startsWith("https://")) {
joinURL = joinURL.replace("https", "bigbluebutton");
joinURL = joinURL.replace("https", "bigbluebuttons");
%>
<script language="javascript" type="text/javascript">

View File

@ -5,6 +5,5 @@ public interface IClientInGW {
void connect(ConnInfo connInfo);
void disconnect(ConnInfo connInfo);
void handleMsgFromClient(ConnInfo connInfo, String json);
void send(String channel, String json);
}

View File

@ -15,35 +15,26 @@ class ClientGWApplication(val msgToClientGW: MsgToClientGW) extends SystemConfig
val log = Logging(system, getClass)
log.debug("*********** meetingManagerChannel = " + meetingManagerChannel)
private val msgFromClientEventBus = new MsgFromClientEventBus
private val jsonMsgToAkkaAppsBus = new JsonMsgToAkkaAppsBus
private val msgFromAkkaAppsEventBus = new MsgFromAkkaAppsEventBus
private val msgToAkkaAppsEventBus = new MsgToAkkaAppsEventBus
private val msgToRedisEventBus = new MsgToRedisEventBus
private val msgToClientEventBus = new MsgToClientEventBus
private val redisPublisher = new RedisPublisher(system)
private val msgSender: MessageSender = new MessageSender(redisPublisher)
private val messageSenderActorRef = system.actorOf(
MessageSenderActor.props(msgSender), "messageSenderActor")
jsonMsgToAkkaAppsBus.subscribe(messageSenderActorRef, toAkkaAppsJsonChannel)
private val meetingManagerActorRef = system.actorOf(
MeetingManagerActor.props(msgToAkkaAppsEventBus, msgToClientEventBus), "meetingManagerActor")
MeetingManagerActor.props(msgToRedisEventBus, msgToClientEventBus), "meetingManagerActor")
msgFromAkkaAppsEventBus.subscribe(meetingManagerActorRef, fromAkkaAppsChannel)
msgFromClientEventBus.subscribe(meetingManagerActorRef, fromClientChannel)
private val receivedJsonMsgBus = new JsonMsgFromAkkaAppsBus
private val msgToAkkaAppsToJsonActor = system.actorOf(
MsgToAkkaAppsToJsonActor.props(jsonMsgToAkkaAppsBus), "msgToAkkaAppsToJsonActor")
private val msgToRedisActor = system.actorOf(
MsgToRedisActor.props(msgSender), "msgToRedisActor")
msgToAkkaAppsEventBus.subscribe(msgToAkkaAppsToJsonActor, toAkkaAppsChannel)
msgToRedisEventBus.subscribe(msgToRedisActor, toRedisChannel)
private val msgToClientJsonActor = system.actorOf(
MsgToClientJsonActor.props(msgToClientGW), "msgToClientJsonActor")
@ -79,11 +70,6 @@ class ClientGWApplication(val msgToClientGW: MsgToClientGW) extends SystemConfig
msgFromClientEventBus.publish(MsgFromClientBusMsg(fromClientChannel, new MsgFromClientMsg(connInfo, json)))
}
def send(channel: String, json: String): Unit = {
//log.debug("Sending message {}", json)
jsonMsgToAkkaAppsBus.publish(JsonMsgToAkkaAppsBusMsg(toAkkaAppsJsonChannel, new JsonMsgToSendToAkkaApps(channel, json)))
}
def shutdown(): Unit = {
system.terminate()
}

View File

@ -15,8 +15,4 @@ class ClientInGW(val clientGWApp: ClientGWApplication) extends IClientInGW {
def handleMsgFromClient(connInfo: ConnInfo, json: String): Unit = {
clientGWApp.handleMsgFromClient(connInfo, json)
}
def send(channel: String, json: String): Unit = {
clientGWApp.send(channel, json)
}
}

View File

@ -1,26 +0,0 @@
package org.bigbluebutton.client
import akka.actor.{Actor, ActorLogging, Props}
import org.bigbluebutton.client.bus.{JsonMsgToAkkaAppsBus, JsonMsgToAkkaAppsBusMsg, JsonMsgToSendToAkkaApps}
import org.bigbluebutton.common2.msgs.BbbCommonEnvJsNodeMsg
import org.bigbluebutton.common2.util.JsonUtil
object MsgToAkkaAppsToJsonActor {
def props(jsonMsgToAkkaAppsBus: JsonMsgToAkkaAppsBus): Props =
Props(classOf[MsgToAkkaAppsToJsonActor], jsonMsgToAkkaAppsBus)
}
class MsgToAkkaAppsToJsonActor(jsonMsgToAkkaAppsBus: JsonMsgToAkkaAppsBus)
extends Actor with ActorLogging with SystemConfiguration {
def receive = {
case msg: BbbCommonEnvJsNodeMsg => handle(msg)
}
def handle(msg: BbbCommonEnvJsNodeMsg): Unit = {
val json = JsonUtil.toJson(msg)
val jsonMsg = JsonMsgToSendToAkkaApps(toAkkaAppsRedisChannel, json)
jsonMsgToAkkaAppsBus.publish(JsonMsgToAkkaAppsBusMsg(toAkkaAppsJsonChannel, jsonMsg))
}
}

View File

@ -0,0 +1,30 @@
package org.bigbluebutton.client
import akka.actor.{Actor, ActorLogging, Props}
import org.bigbluebutton.common2.msgs.BbbCommonEnvJsNodeMsg
import org.bigbluebutton.common2.util.JsonUtil
import org.bigbluebutton.client.endpoint.redis.MessageSender
import org.bigbluebutton.common2.msgs.LookUpUserReqMsg
object MsgToRedisActor {
def props(msgSender: MessageSender): Props =
Props(classOf[MsgToRedisActor], msgSender)
}
class MsgToRedisActor(msgSender: MessageSender)
extends Actor with ActorLogging with SystemConfiguration {
def receive = {
case msg: BbbCommonEnvJsNodeMsg => handle(msg)
}
def handle(msg: BbbCommonEnvJsNodeMsg): Unit = {
val json = JsonUtil.toJson(msg)
msg.envelope.name match {
case LookUpUserReqMsg.NAME => msgSender.send(toThirdPartyRedisChannel, json)
case _ => msgSender.send(toAkkaAppsRedisChannel, json)
}
}
}

View File

@ -12,12 +12,12 @@ trait SystemConfiguration {
lazy val toAkkaAppsRedisChannel = Try(config.getString("redis.toAkkaAppsRedisChannel")).getOrElse("to-akka-apps-redis-channel")
lazy val fromAkkaAppsRedisChannel = Try(config.getString("redis.fromAkkaAppsRedisChannel")).getOrElse("from-akka-apps-redis-channel")
lazy val meetingManagerChannel = Try(config.getString("eventBus.meetingManagerChannel")).getOrElse("FOOOOOOOOO")
lazy val toThirdPartyRedisChannel = Try(config.getString("redis.toThirdPartyRedisChannel")).getOrElse("to-third-party-redis-channel")
lazy val fromThirdPartyRedisChannel = Try(config.getString("redis.fromThirdPartyRedisChannel")).getOrElse("from-third-party-redis-channel")
lazy val fromAkkaAppsChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-channel")
lazy val toAkkaAppsChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-channel")
lazy val toRedisChannel = Try(config.getString("eventBus.toRedisChannel")).getOrElse("to-redis-channel")
lazy val fromClientChannel = Try(config.getString("eventBus.fromClientChannel")).getOrElse("from-client-channel")
lazy val toClientChannel = Try(config.getString("eventBus.toClientChannel")).getOrElse("to-client-channel")
lazy val toAkkaAppsJsonChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-json-channel")
lazy val fromAkkaAppsJsonChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-json-channel")
lazy val fromAkkaAppsWbRedisChannel = Try(config.getString("redis.fromAkkaAppsWbRedisChannel")).getOrElse("from-akka-apps-wb-redis-channel")

View File

@ -1,31 +0,0 @@
package org.bigbluebutton.client.bus
import akka.actor.ActorRef
import akka.event.{EventBus, LookupClassification}
case class JsonMsgToSendToAkkaApps(channel: String, json: String)
case class JsonMsgToAkkaAppsBusMsg(val topic: String, payload: JsonMsgToSendToAkkaApps)
class JsonMsgToAkkaAppsBus extends EventBus with LookupClassification {
type Event = JsonMsgToAkkaAppsBusMsg
type Classifier = String
type Subscriber = ActorRef
// is used for extracting the classifier from the incoming events
override protected def classify(event: Event): Classifier = event.topic
// will be invoked for each event for all subscribers which registered themselves
// for the events classifier
override protected def publish(event: Event, subscriber: Subscriber): Unit = {
subscriber ! event.payload
}
// must define a full order over the subscribers, expressed as expected from
// `java.lang.Comparable.compare`
override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
a.compareTo(b)
// determines the initial size of the index data structure
// used internally (i.e. the expected number of different classifiers)
override protected def mapSize: Int = 128
}

View File

@ -4,10 +4,10 @@ import akka.actor.ActorRef
import akka.event.{EventBus, LookupClassification}
import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg}
case class MsgToAkkaApps(val topic: String, val payload: BbbCommonEnvJsNodeMsg)
case class MsgToRedis(val topic: String, val payload: BbbCommonEnvJsNodeMsg)
class MsgToAkkaAppsEventBus extends EventBus with LookupClassification {
type Event = MsgToAkkaApps
class MsgToRedisEventBus extends EventBus with LookupClassification {
type Event = MsgToRedis
type Classifier = String
type Subscriber = ActorRef

View File

@ -15,7 +15,7 @@ import redis.api.servers.ClientSetname
object AppsRedisSubscriberActor extends SystemConfiguration {
val channels = Seq(fromAkkaAppsRedisChannel, fromAkkaAppsWbRedisChannel, fromAkkaAppsChatRedisChannel, fromAkkaAppsPresRedisChannel)
val channels = Seq(fromAkkaAppsRedisChannel, fromAkkaAppsWbRedisChannel, fromAkkaAppsChatRedisChannel, fromAkkaAppsPresRedisChannel, fromThirdPartyRedisChannel)
val patterns = Seq("bigbluebutton:from-bbb-apps:*")
def props(jsonMsgBus: JsonMsgFromAkkaAppsBus): Props =

View File

@ -34,6 +34,7 @@ object AllowedMessageNames {
GetGuestPolicyReqMsg.NAME,
SetGuestPolicyCmdMsg.NAME,
GuestsWaitingApprovedMsg.NAME,
UserActivitySignCmdMsg.NAME,
// Webcams
GetWebcamsOnlyForModeratorReqMsg.NAME,
@ -50,6 +51,7 @@ object AllowedMessageNames {
SendGroupChatMessageMsg.NAME,
ClearPublicChatHistoryPubMsg.NAME,
CreateGroupChatReqMsg.NAME,
UserTypingPubMsg.NAME,
// Presentation Messages
ResizeAndMovePagePubMsg.NAME,
@ -106,6 +108,9 @@ object AllowedMessageNames {
BreakoutRoomsListMsg.NAME,
// System
ClientToServerLatencyTracerMsg.NAME
ClientToServerLatencyTracerMsg.NAME,
// Third-party Message
LookUpUserReqMsg.NAME
)
}

View File

@ -1,16 +1,16 @@
package org.bigbluebutton.client.meeting
import akka.actor.ActorContext
import org.bigbluebutton.client.bus.{MsgToAkkaAppsEventBus, MsgToClientEventBus}
import org.bigbluebutton.client.bus.{MsgToRedisEventBus, MsgToClientEventBus}
object Meeting {
def apply(meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
def apply(meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus)(implicit context: ActorContext) =
new Meeting(meetingId, msgToAkkaAppsEventBus, msgToClientEventBus)(context)
new Meeting(meetingId, msgToRedisEventBus, msgToClientEventBus)(context)
}
class Meeting(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
class Meeting(val meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus)(implicit val context: ActorContext) {
val actorRef = context.actorOf(MeetingActor.props(meetingId, msgToAkkaAppsEventBus, msgToClientEventBus), meetingId)
val actorRef = context.actorOf(MeetingActor.props(meetingId, msgToRedisEventBus, msgToClientEventBus), meetingId)
}

View File

@ -6,12 +6,12 @@ import org.bigbluebutton.client.bus._
import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg, DisconnectAllClientsSysMsg, MessageTypes}
object MeetingActor {
def props(meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
def props(meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus): Props =
Props(classOf[MeetingActor], meetingId, msgToAkkaAppsEventBus, msgToClientEventBus)
Props(classOf[MeetingActor], meetingId, msgToRedisEventBus, msgToClientEventBus)
}
class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
class MeetingActor(val meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus)
extends Actor with ActorLogging
with SystemConfiguration{
@ -27,7 +27,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv
}
private def createUser(id: String): User = {
User(id, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus)
User(id, msgToRedisEventBus, meetingId, msgToClientEventBus)
}
def handleConnectMsg(msg: ConnectMsg): Unit = {

View File

@ -6,12 +6,12 @@ import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg, MessageTypes}
object MeetingManagerActor {
def props(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
def props(msgToAkkaAppsEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus): Props =
Props(classOf[MeetingManagerActor], msgToAkkaAppsEventBus, msgToClientEventBus)
}
class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
class MeetingManagerActor(msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus)
extends Actor with ActorLogging {
@ -26,7 +26,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
}
def createMeeting(meetingId: String): Meeting = {
Meeting(meetingId, msgToAkkaAppsEventBus, msgToClientEventBus)
Meeting(meetingId, msgToRedisEventBus, msgToClientEventBus)
}
def handleConnectMsg(msg: ConnectMsg): Unit = {

View File

@ -1,21 +1,21 @@
package org.bigbluebutton.client.meeting
import akka.actor.ActorContext
import org.bigbluebutton.client.bus.{MsgToAkkaAppsEventBus, MsgToClientEventBus}
import org.bigbluebutton.client.bus.{MsgToRedisEventBus, MsgToClientEventBus}
object User {
def apply(userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String,
msgToClientEventBus: MsgToClientEventBus) (implicit context: ActorContext): User =
new User(userId, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus)(context)
new User(userId, msgToRedisEventBus, meetingId, msgToClientEventBus)(context)
}
class User(val userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String,
msgToClientEventBus: MsgToClientEventBus)(implicit val context: ActorContext) {
val actorRef = context.actorOf(UserActor.props(userId, msgToAkkaAppsEventBus,
val actorRef = context.actorOf(UserActor.props(userId, msgToRedisEventBus,
meetingId, msgToClientEventBus), meetingId + "-" + userId)
}

View File

@ -11,14 +11,14 @@ import scala.util.{Failure, Success}
object UserActor {
def props(userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String,
msgToClientEventBus: MsgToClientEventBus): Props =
Props(classOf[UserActor], userId, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus)
Props(classOf[UserActor], userId, msgToRedisEventBus, meetingId, msgToClientEventBus)
}
class UserActor(val userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String,
msgToClientEventBus: MsgToClientEventBus)
extends Actor with ActorLogging with SystemConfiguration {
@ -133,8 +133,8 @@ class UserActor(val userId: String,
for {
jsonNode <- convertToJsonNode(json)
} yield {
val akkaMsg = BbbCommonEnvJsNodeMsg(envelope, jsonNode)
msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, akkaMsg))
val jsNodeMsg = BbbCommonEnvJsNodeMsg(envelope, jsonNode)
msgToRedisEventBus.publish(MsgToRedis(toRedisChannel, jsNodeMsg))
}
}
case None =>

View File

@ -43,6 +43,7 @@ package org.bigbluebutton.clientcheck.service
private static var RECORD_MOCK:Boolean=false;
private static var EXTERNAL_USER_ID_MOCK:String="123456";
private static var INTERNAL_USER_ID_MOCK:String="654321";
private static var CLIENT_CONN_ID:String="client-conn-id";
private static var LOCK_ON_MOCK:Boolean=true;
public function init():void
@ -60,7 +61,7 @@ package org.bigbluebutton.clientcheck.service
// sip has a different way of connecting to the red5 server, need to fake connection data.
if (systemConfiguration.rtmpApps[i].applicationUri.indexOf("sip") > 0)
{
_netConnection.connect(systemConfiguration.rtmpApps[i].applicationUri, ROOM_MOCK, EXTERNAL_USER_ID_MOCK, USER_NAME_MOCK);
_netConnection.connect(systemConfiguration.rtmpApps[i].applicationUri, ROOM_MOCK, EXTERNAL_USER_ID_MOCK, USER_NAME_MOCK, INTERNAL_USER_ID_MOCK, CLIENT_CONN_ID);
continue;
}
else

View File

@ -0,0 +1,13 @@
FROM sbt:0.13.8
ARG COMMON_VERSION
COPY . /bbb-common-message
RUN cd /bbb-common-message \
&& sed -i "s|\(version := \)\".*|\1\"$COMMON_VERSION\"|g" build.sbt \
&& echo 'publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))' | tee -a build.sbt \
&& sbt compile \
&& sbt publish \
&& sbt publishLocal

View File

@ -2,13 +2,14 @@ package org.bigbluebutton.common2.domain
case class ConfigProps(defaultConfigToken: String, config: String)
case class DurationProps(duration: Int, createdTime: Long, createdDate: String, maxInactivityTimeoutMinutes: Int,
warnMinutesBeforeMax: Int, meetingExpireIfNoUserJoinedInMinutes: Int,
meetingExpireWhenLastUserLeftInMinutes: Int)
case class DurationProps(duration: Int, createdTime: Long, createdDate: String,
maxInactivityTimeoutMinutes: Int, warnMinutesBeforeMax: Int,
meetingExpireIfNoUserJoinedInMinutes: Int, meetingExpireWhenLastUserLeftInMinutes: Int,
userInactivityInspectTimerInMinutes: Int, userInactivityThresholdInMinutes: Int, userActivitySignResponseDelayInMinutes: Int)
case class MeetingProp(name: String, extId: String, intId: String, isBreakout: Boolean)
case class BreakoutProps(parentId: String, sequence: Int, breakoutRooms: Vector[String])
case class BreakoutProps(parentId: String, sequence: Int, freeJoin: Boolean, breakoutRooms: Vector[String])
case class PasswordProp(moderatorPass: String, viewerPass: String)

View File

@ -8,13 +8,13 @@ package org.bigbluebutton.common2.msgs
object BreakoutRoomJoinURLEvtMsg { val NAME = "BreakoutRoomJoinURLEvtMsg" }
case class BreakoutRoomJoinURLEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomJoinURLEvtMsgBody) extends BbbCoreMsg
case class BreakoutRoomJoinURLEvtMsgBody(parentId: String, breakoutId: String, externalId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String)
userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String)
// Outgoing messages
object BreakoutRoomsListEvtMsg { val NAME = "BreakoutRoomsListEvtMsg" }
case class BreakoutRoomsListEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListEvtMsgBody) extends BbbCoreMsg
case class BreakoutRoomsListEvtMsgBody(meetingId: String, rooms: Vector[BreakoutRoomInfo], roomsReady: Boolean)
case class BreakoutRoomInfo(name: String, externalId: String, breakoutId: String, sequence: Int)
case class BreakoutRoomInfo(name: String, externalId: String, breakoutId: String, sequence: Int, freeJoin:Boolean)
object BreakoutRoomsListMsg { val NAME = "BreakoutRoomsListMsg" }
case class BreakoutRoomsListMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListMsgBody) extends StandardMsg
@ -39,7 +39,7 @@ package org.bigbluebutton.common2.msgs
case class CreateBreakoutRoomSysCmdMsg(header: BbbCoreBaseHeader,
body: CreateBreakoutRoomSysCmdMsgBody) extends BbbCoreMsg
case class CreateBreakoutRoomSysCmdMsgBody(meetingId: String, room: BreakoutRoomDetail)
case class BreakoutRoomDetail(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer,
case class BreakoutRoomDetail(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer, freeJoin:Boolean,
voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String,
sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean)
@ -49,7 +49,7 @@ package org.bigbluebutton.common2.msgs
object CreateBreakoutRoomsCmdMsg { val NAME = "CreateBreakoutRoomsCmdMsg" }
case class CreateBreakoutRoomsCmdMsg(header: BbbClientMsgHeader, body: CreateBreakoutRoomsCmdMsgBody) extends StandardMsg
case class CreateBreakoutRoomsCmdMsgBody(meetingId: String, durationInMinutes: Int, record: Boolean, rooms: Vector[BreakoutRoomMsgBody])
case class BreakoutRoomMsgBody(name: String, sequence: Int, users: Vector[String])
case class BreakoutRoomMsgBody(name: String, sequence: Int, freeJoin:Boolean, users: Vector[String])
// Sent by user to request ending all the breakout rooms
object EndAllBreakoutRoomsMsg { val NAME = "EndAllBreakoutRoomsMsg" }
@ -69,7 +69,7 @@ package org.bigbluebutton.common2.msgs
object RequestBreakoutJoinURLRespMsg { val NAME = "RequestBreakoutJoinURLRespMsg" }
case class RequestBreakoutJoinURLRespMsg(header: BbbClientMsgHeader, body: RequestBreakoutJoinURLRespMsgBody) extends BbbCoreMsg
case class RequestBreakoutJoinURLRespMsgBody(parentId: String, breakoutId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String)
userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String)
object TransferUserToMeetingEvtMsg { val NAME = "TransferUserToMeetingEvtMsg" }
@ -89,7 +89,7 @@ case class RequestBreakoutJoinURLRespMsgBody(parentId: String, breakoutId: Strin
case class BreakoutUserVO(id: String, name: String)
case class BreakoutRoomVO(id: String, externalId: String, name: String, parentId: String,
sequence: Int, voiceConf: String,
sequence: Int, freeJoin: Boolean, voiceConf: String,
assignedUsers: Vector[String], users: Vector[BreakoutUserVO])

View File

@ -1,6 +1,5 @@
package org.bigbluebutton.common2.msgs
/* In Messages */
object GetChatHistoryReqMsg { val NAME = "GetChatHistoryReqMsg" }
case class GetChatHistoryReqMsg(header: BbbClientMsgHeader, body: GetChatHistoryReqMsgBody) extends StandardMsg
@ -35,5 +34,9 @@ object ClearPublicChatHistoryEvtMsg { val NAME = "ClearPublicChatHistoryEvtMsg"}
case class ClearPublicChatHistoryEvtMsg(header: BbbClientMsgHeader, body: ClearPublicChatHistoryEvtMsgBody) extends StandardMsg
case class ClearPublicChatHistoryEvtMsgBody(chatId: String)
object UserTypingEvtMsg { val NAME = "UserTypingEvtMsg" }
case class UserTypingEvtMsg(header: BbbClientMsgHeader, body: UserTypingEvtMsgBody) extends StandardMsg
case class UserTypingEvtMsgBody(chatId: String, userId: String)
case class ChatMessageVO(fromUserId: String, fromUsername: String, fromColor: String, fromTime: Long, fromTimezoneOffset: Int,
toUserId: String, toUsername: String, message: String)

Some files were not shown because too many files have changed in this diff Show More