Merge branch 'ritzalam-work-on-meeting-lifcycle-2' into bbb-2x-mconf

This commit is contained in:
Richard Alam 2017-07-20 18:19:16 -07:00
commit d1cc692791
70 changed files with 1704 additions and 1582 deletions

View File

@ -59,21 +59,21 @@ class BigBlueButtonActor(
} }
def receive = { def receive = {
// 2x messages // Internal messages
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg) case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg)
case msg: ValidateAuthToken => handleValidateAuthToken(msg) // 2x messages
case _ => // do nothing case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
case _ => // do nothing
} }
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = { private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
msg.core match { msg.core match {
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m) case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m) case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m) case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
case m: PubSubPingSysReqMsg => handlePubSubPingSysReqMsg(m) case m: PubSubPingSysReqMsg => handlePubSubPingSysReqMsg(m)
case m: DestroyMeetingSysCmdMsg => handleDestroyMeeting(m) case _ => log.warning("Cannot handle " + msg.envelope.name)
case _ => log.warning("Cannot handle " + msg.envelope.name)
} }
} }
@ -125,68 +125,38 @@ class BigBlueButtonActor(
}) })
} }
private def handleValidateAuthToken(msg: ValidateAuthToken) {
for {
m <- RunningMeetings.findWithId(meetings, msg.meetingID)
} yield {
m.actorRef forward (msg)
}
//meetings.get(msg.meetingID) foreach { m =>
// m.actorRef ! msg
// val future = m.actorRef.ask(msg)(5 seconds)
// future onComplete {
// case Success(result) => {
// log.info("Validate auth token response. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
// /**
// * Received a reply from MeetingActor which means hasn't hung!
// * Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
// * audit to check whether the actor is still alive. (ralam feb 25, 2015)
// */
// }
// case Failure(failure) => {
// log.warning("Validate auth token timeout. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
// outGW.send(new ValidateAuthTokenTimedOut(msg.meetingID, msg.userId, msg.token, false, msg.correlationId))
// }
// }
//}
}
private def handlePubSubPingSysReqMsg(msg: PubSubPingSysReqMsg): Unit = { private def handlePubSubPingSysReqMsg(msg: PubSubPingSysReqMsg): Unit = {
val event = MsgBuilder.buildPubSubPongSysRespMsg(msg.body.system, msg.body.timestamp) val event = MsgBuilder.buildPubSubPongSysRespMsg(msg.body.system, msg.body.timestamp)
outGW.send(event) outGW.send(event)
} }
private def handleDestroyMeeting(msg: DestroyMeetingSysCmdMsg): Unit = { private def handleDestroyMeeting(msg: DestroyMeetingInternalMsg): Unit = {
for { for {
m <- RunningMeetings.findWithId(meetings, msg.body.meetingId) m <- RunningMeetings.findWithId(meetings, msg.meetingId)
m2 <- RunningMeetings.remove(meetings, msg.body.meetingId) m2 <- RunningMeetings.remove(meetings, msg.meetingId)
} yield { } yield {
// send the message for MeetingActor to handle too /** Unsubscribe to meeting and voice events. **/
m.actorRef ! msg eventBus.unsubscribe(m.actorRef, m.props.meetingProp.intId)
eventBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf)
eventBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf)
// Delay sending DisconnectAllUsers because of RTMPT connection being dropped before UserEject message arrives to the client bbbMsgBus.unsubscribe(m.actorRef, m.props.meetingProp.intId)
bbbMsgBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf)
bbbMsgBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf)
// Delay sending DisconnectAllUsers to allow messages to reach the client
// before the connections are closed.
context.system.scheduler.scheduleOnce(Duration.create(2500, TimeUnit.MILLISECONDS)) { context.system.scheduler.scheduleOnce(Duration.create(2500, TimeUnit.MILLISECONDS)) {
// Disconnect all clients // Disconnect all clients
val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.body.meetingId) val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.meetingId)
outGW.send(disconnectEvnt) outGW.send(disconnectEvnt)
log.info("Destroyed meetingId={}", msg.body.meetingId) log.info("Destroyed meetingId={}", msg.meetingId)
val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.body.meetingId) val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.meetingId)
outGW.send(destroyedEvent) outGW.send(destroyedEvent)
/** Unsubscribe to meeting and voice events. **/
eventBus.unsubscribe(m.actorRef, m.props.meetingProp.intId)
eventBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf)
eventBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf)
bbbMsgBus.unsubscribe(m.actorRef, m.props.meetingProp.intId)
bbbMsgBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf)
bbbMsgBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf)
// Stop the meeting actor. // Stop the meeting actor.
context.stop(m.actorRef) context.stop(m.actorRef)
} }

View File

@ -78,7 +78,7 @@ class BigBlueButtonInGW(
eventBus.publish( eventBus.publish(
BigBlueButtonEvent( BigBlueButtonEvent(
"meeting-manager", "meeting-manager",
new DestroyMeeting( new DestroyMeetingInternalMsg(
meetingID meetingID
) )
) )

View File

@ -18,13 +18,18 @@ case class IsMeetingActorAliveMessage(meetingId: String) extends InMessage
case class KeepAliveMessage(aliveID: String) extends InMessage case class KeepAliveMessage(aliveID: String) extends InMessage
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Meeting // Internal Messages
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
case class MonitorNumberOfUsers(meetingID: String) extends InMessage case class MonitorNumberOfUsersInternalMsg(meetingID: String) extends InMessage
case class SendTimeRemainingUpdate(meetingId: String) extends InMessage case class SendTimeRemainingUpdate(meetingId: String) extends InMessage
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
case class DestroyMeeting(meetingID: String) extends InMessage case class DestroyMeetingInternalMsg(meetingId: String) extends InMessage
case class BreakoutRoomEndedInternalMsg(meetingId: String) extends InMessage
//////////////////////////////////////////////////////////////////////////////
// Meeting
/////////////////////////////////////////////////////////////////////////////
case class StartMeeting(meetingID: String) extends InMessage case class StartMeeting(meetingID: String) extends InMessage
case class EndMeeting(meetingId: String) extends InMessage case class EndMeeting(meetingId: String) extends InMessage
case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String, Boolean]) extends InMessage case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String, Boolean]) extends InMessage

View File

@ -1,11 +1,12 @@
package org.bigbluebutton.core.apps package org.bigbluebutton.core.apps
import org.bigbluebutton.core.running.MeetingActor import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.message.handlers.guests.{ GetGuestsWaitingApprovalReqMsgHdlr, GuestsWaitingApprovedMsgHdlr, SetGuestPolicyMsgHdlr } import org.bigbluebutton.core2.message.handlers.guests.{ GetGuestPolicyReqMsgHdlr, GetGuestsWaitingApprovalReqMsgHdlr, GuestsWaitingApprovedMsgHdlr, SetGuestPolicyMsgHdlr }
trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr
with GuestsWaitingApprovedMsgHdlr with GuestsWaitingApprovedMsgHdlr
with SetGuestPolicyMsgHdlr { with SetGuestPolicyMsgHdlr
with GetGuestPolicyReqMsgHdlr {
this: MeetingActor => this: MeetingActor =>

View File

@ -11,8 +11,6 @@ trait PresentationConversionCompletedPubMsgHdlr {
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handlePresentationConversionCompletedPubMsg(msg: PresentationConversionCompletedSysPubMsg): Unit = { def handlePresentationConversionCompletedPubMsg(msg: PresentationConversionCompletedSysPubMsg): Unit = {
log.debug("**************** !!!!!PresentationConversionCompletedPubMsg ")
def broadcastEvent(msg: PresentationConversionCompletedSysPubMsg): Unit = { def broadcastEvent(msg: PresentationConversionCompletedSysPubMsg): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing)

View File

@ -9,7 +9,7 @@ trait PresentationConversionUpdatePubMsgHdlr {
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handlePresentationConversionUpdatePubMsg(msg: PresentationConversionUpdateSysPubMsg): Unit = { def handlePresentationConversionUpdatePubMsg(msg: PresentationConversionUpdateSysPubMsg): Unit = {
log.debug("**************** !!!!!PresentationConversionUpdateSysPubMsg " + msg.body.messageKey)
def broadcastEvent(msg: PresentationConversionUpdateSysPubMsg): Unit = { def broadcastEvent(msg: PresentationConversionUpdateSysPubMsg): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing)

View File

@ -9,7 +9,6 @@ trait PresentationPageGeneratedPubMsgHdlr {
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handlePresentationPageGeneratedPubMsg(msg: PresentationPageGeneratedSysPubMsg): Unit = { def handlePresentationPageGeneratedPubMsg(msg: PresentationPageGeneratedSysPubMsg): Unit = {
log.debug("**************** !!!!!PresentationPageGeneratedSysPubMsg " + msg.body.messageKey)
def broadcastEvent(msg: PresentationPageGeneratedSysPubMsg): Unit = { def broadcastEvent(msg: PresentationPageGeneratedSysPubMsg): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -1,9 +1,9 @@
package org.bigbluebutton.core2.message.handlers.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.models.Users2x import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, MeetingActor } import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
trait ChangeUserEmojiCmdMsgHdlr { trait ChangeUserEmojiCmdMsgHdlr {
this: BaseMeetingActor => this: BaseMeetingActor =>

View File

@ -11,8 +11,8 @@ trait GetUsersMeetingReqMsgHdlr extends HandlerHelpers {
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handleGetUsersMeetingReqMsg(msg: GetUsersMeetingReqMsg): Unit = { def handleGetUsersMeetingReqMsg(msg: GetUsersMeetingReqMsg): Unit = {
sendAllUsersInMeeting(outGW, msg.body.userId, liveMeeting) sendAllUsersInMeeting(msg.body.userId)
sendAllVoiceUsersInMeeting(outGW, msg.body.userId, liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId) sendAllVoiceUsersInMeeting(msg.body.userId, liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId)
sendAllWebcamStreams(outGW, msg.body.userId, liveMeeting.webcams, liveMeeting.props.meetingProp.intId) sendAllWebcamStreams(outGW, msg.body.userId, liveMeeting.webcams, liveMeeting.props.meetingProp.intId)
} }
} }

View File

@ -1,4 +1,4 @@
package org.bigbluebutton.core2.message.handlers.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway

View File

@ -2,56 +2,25 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.api.{ EndMeeting, LogoutEndMeeting } import org.bigbluebutton.core.bus.IncomingEventBus
import org.bigbluebutton.core.domain.MeetingEndReason
import org.bigbluebutton.core.models.{ Roles, Users2x } import org.bigbluebutton.core.models.{ Roles, Users2x }
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.MeetingStatus2x
trait LogoutAndEndMeetingCmdMsgHdlr { trait LogoutAndEndMeetingCmdMsgHdlr {
this: UsersApp => this: UsersApp =>
val liveMeeting: LiveMeeting val liveMeeting: LiveMeeting
val outGW: OutMessageGateway val outGW: OutMessageGateway
val eventBus: IncomingEventBus
def handleLogoutAndEndMeetingCmdMsg(msg: LogoutAndEndMeetingCmdMsg) { def handleLogoutAndEndMeetingCmdMsg(msg: LogoutAndEndMeetingCmdMsg) {
for { for {
u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield { } yield {
if (u.role == Roles.MODERATOR_ROLE) { if (u.role == Roles.MODERATOR_ROLE) {
endMeeting() sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_AFTER_USER_LOGGED_OUT, eventBus, outGW, liveMeeting)
} }
} }
def endMeeting(): Unit = {
def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing)
val body = MeetingEndingEvtMsgBody(meetingId)
val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used")
val event = MeetingEndingEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val endingEvent = buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId)
// Broadcast users the meeting will end
outGW.send(endingEvent)
MeetingStatus2x.meetingHasEnded(liveMeeting.status)
def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing)
val body = MeetingEndedEvtMsgBody(meetingId)
val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME)
val event = MeetingEndedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId)
outGW.send(endedEvnt)
}
} }
} }

View File

@ -1,9 +1,10 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.domain.MeetingInactivityTracker import org.bigbluebutton.core.domain.{ MeetingInactivityTracker, MeetingState2x }
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, MeetingInactivityTrackerHelper } import org.bigbluebutton.core.running.{ LiveMeeting }
trait MeetingActivityResponseCmdMsgHdlr { trait MeetingActivityResponseCmdMsgHdlr {
this: UsersApp => this: UsersApp =>
@ -12,14 +13,31 @@ trait MeetingActivityResponseCmdMsgHdlr {
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handleMeetingActivityResponseCmdMsg( def handleMeetingActivityResponseCmdMsg(
msg: MeetingActivityResponseCmdMsg, msg: MeetingActivityResponseCmdMsg,
tracker: MeetingInactivityTracker state: MeetingState2x
): MeetingInactivityTracker = { ): MeetingState2x = {
MeetingInactivityTrackerHelper.processMeetingActivityResponse( processMeetingActivityResponse(liveMeeting.props, outGW, msg)
props = liveMeeting.props, MeetingInactivityTracker.resetWarningSentAndTimestamp(state)
outGW, }
msg,
tracker def processMeetingActivityResponse(
) props: DefaultProps,
outGW: OutMessageGateway,
msg: MeetingActivityResponseCmdMsg
): Unit = {
def buildMeetingIsActiveEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingIsActiveEvtMsg.NAME, routing)
val body = MeetingIsActiveEvtMsgBody(meetingId)
val header = BbbClientMsgHeader(MeetingIsActiveEvtMsg.NAME, meetingId, "not-used")
val event = MeetingIsActiveEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildMeetingIsActiveEvtMsg(props.meetingProp.intId)
outGW.send(event)
} }
} }

View File

@ -1,4 +1,4 @@
package org.bigbluebutton.core2.message.handlers.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.MuteUserCmdMsg import org.bigbluebutton.common2.msgs.MuteUserCmdMsg
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway

View File

@ -13,22 +13,7 @@ trait RegisterUserReqMsgHdlr {
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handleRegisterUserReqMsg(msg: RegisterUserReqMsg): Unit = { def handleRegisterUserReqMsg(msg: RegisterUserReqMsg): Unit = {
log.debug("****** RECEIVED RegisterUserReqMsg msg {}", msg) log.debug("RECEIVED RegisterUserReqMsg msg {}", msg)
if (MeetingStatus2x.hasMeetingEnded(liveMeeting.status)) {
// Check first if the meeting has ended and the user refreshed the client to re-connect.
log.info("Register user failed. Mmeeting has ended. meetingId=" + liveMeeting.props.meetingProp.intId +
" userId=" + msg.body.intUserId)
} else {
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, msg.body.guest, liveMeeting.registeredUsers)
log.info("Register user success. meetingId=" + liveMeeting.props.meetingProp.intId
+ " userId=" + msg.body.extUserId + " user=" + regUser)
val event = buildUserRegisteredRespMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.name, regUser.role)
outGW.send(event)
}
def buildUserRegisteredRespMsg(meetingId: String, userId: String, name: String, role: String): BbbCommonEnvCoreMsg = { def buildUserRegisteredRespMsg(meetingId: String, userId: String, name: String, role: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
@ -38,5 +23,18 @@ trait RegisterUserReqMsgHdlr {
val event = UserRegisteredRespMsg(header, body) val event = UserRegisteredRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
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, msg.body.guest)
RegisteredUsers.add(liveMeeting.registeredUsers, regUser)
log.info("Register user success. meetingId=" + liveMeeting.props.meetingProp.intId
+ " userId=" + msg.body.extUserId + " user=" + regUser)
val event = buildUserRegisteredRespMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.name, regUser.role)
outGW.send(event)
} }
} }

View File

@ -1,4 +1,4 @@
package org.bigbluebutton.core2.message.handlers.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway

View File

@ -1,4 +1,4 @@
package org.bigbluebutton.core2.message.handlers.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway

View File

@ -1,4 +1,4 @@
package org.bigbluebutton.core2.message.handlers.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway

View File

@ -1,4 +1,4 @@
package org.bigbluebutton.core2.message.handlers.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway

View File

@ -0,0 +1,19 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting }
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
this: BaseMeetingActor =>
val liveMeeting: LiveMeeting
val outGW: OutMessageGateway
def handleUserJoinMeetingReqMsg(msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = {
userJoinMeeting(outGW, msg.body.authToken, liveMeeting, state)
}
}

View File

@ -2,8 +2,10 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x }
import org.bigbluebutton.core.models.Users2x import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.MeetingActor import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.message.senders.MsgBuilder import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserLeaveReqMsgHdlr { trait UserLeaveReqMsgHdlr {
@ -11,20 +13,26 @@ trait UserLeaveReqMsgHdlr {
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handleUserLeaveReqMsg(msg: UserLeaveReqMsg): Unit = { def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = {
for { for {
u <- Users2x.remove(liveMeeting.users2x, msg.body.userId) u <- Users2x.remove(liveMeeting.users2x, msg.body.userId)
} yield { } yield {
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u) log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u)
captionApp2x.handleUserLeavingMsg(msg.body.userId) captionApp2x.handleUserLeavingMsg(msg.body.userId)
liveMeeting.startCheckingIfWeNeedToEndVoiceConf()
stopAutoStartedRecording() stopAutoStartedRecording()
// send a user left event for the clients to update // send a user left event for the clients to update
val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId) val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId)
outGW.send(userLeftMeetingEvent) outGW.send(userLeftMeetingEvent)
log.info("User left meetingId=" + liveMeeting.props.meetingProp.intId + " userId=" + msg.body.userId) log.info("User left meetingId=" + liveMeeting.props.meetingProp.intId + " userId=" + msg.body.userId)
}
if (Users2x.numUsers(liveMeeting.users2x) == 0) {
MeetingExpiryTracker.setLastUserLeftOn(state, TimeUtil.timeNowInSeconds())
} else {
state
} }
} }

View File

@ -3,12 +3,13 @@ package org.bigbluebutton.core.apps.users
import akka.actor.ActorContext import akka.actor.ActorContext
import akka.event.Logging import akka.event.Logging
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.bus.IncomingEventBus
import org.bigbluebutton.core.running.LiveMeeting import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.message.handlers.users.ValidateAuthTokenReqMsgHdlr
class UsersApp( class UsersApp(
val liveMeeting: LiveMeeting, val liveMeeting: LiveMeeting,
val outGW: OutMessageGateway val outGW: OutMessageGateway,
val eventBus: IncomingEventBus
)(implicit val context: ActorContext) )(implicit val context: ActorContext)
extends ValidateAuthTokenReqMsgHdlr extends ValidateAuthTokenReqMsgHdlr

View File

@ -1,7 +1,6 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.core.running.MeetingActor import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.message.handlers.users.{ ChangeUserEmojiCmdMsgHdlr, ValidateAuthTokenReqMsgHdlr }
trait UsersApp2x trait UsersApp2x
extends UserLeaveReqMsgHdlr extends UserLeaveReqMsgHdlr

View File

@ -0,0 +1,135 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.bus.IncomingEventBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMessageGateway
val eventBus: IncomingEventBus
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = {
log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg)
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers)
regUser match {
case Some(u) =>
if (noNeedForApproval(u)) {
userValidatedAndNoNeedToWaitForApproval(u, state)
} else {
goThroughGuestPolicy(liveMeeting.guestsWaiting, u, state)
}
case None =>
validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId,
userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state)
}
}
def noNeedForApproval(user: RegisteredUser): Boolean = {
!user.guest || (user.guest && !user.waitingForAcceptance)
}
def goThroughGuestPolicy(guestsWaiting: GuestsWaiting, user: RegisteredUser, state: MeetingState2x): MeetingState2x = {
if (doesNotHaveToWaitForApproval(guestsWaiting, user)) {
userValidatedAndNoNeedToWaitForApproval(user, state)
} else {
userValidatedButNeedToWaitForApproval(user, state)
}
}
def doesNotHaveToWaitForApproval(guestsWaiting: GuestsWaiting, user: RegisteredUser): Boolean = {
val guestPolicyType = GuestsWaiting.getGuestPolicy(guestsWaiting).policy
(guestPolicyType == GuestPolicyType.ALWAYS_ACCEPT) ||
(guestPolicyType == GuestPolicyType.ASK_MODERATOR && user.guest && !user.waitingForAcceptance)
}
def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String,
valid: Boolean, waitForApproval: Boolean, state: MeetingState2x): MeetingState2x = {
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval)
Sender.send(outGW, event)
// TODO: Should disconnect user here.
state
}
def sendValidateAuthTokenRespMsg(meetingId: String, userId: String, authToken: String,
valid: Boolean, waitForApproval: Boolean): Unit = {
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval)
Sender.send(outGW, event)
}
def userValidatedButNeedToWaitForApproval(user: RegisteredUser, state: MeetingState2x): MeetingState2x = {
val meetingId = liveMeeting.props.meetingProp.intId
sendValidateAuthTokenRespMsg(meetingId, user.id, user.authToken, valid = true, waitForApproval = false)
val guest = GuestWaiting(user.id, user.name, user.role)
addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting)
notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, meetingId)
state
}
def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = {
GuestsWaiting.add(guestsWaitingList, guest)
}
def userValidatedAndNoNeedToWaitForApproval(user: RegisteredUser, state: MeetingState2x): MeetingState2x = {
val meetingId = liveMeeting.props.meetingProp.intId
sendValidateAuthTokenRespMsg(
meetingId,
userId = user.id, authToken = user.authToken, valid = true, waitForApproval = false
)
// TODO: REMOVE Temp only so we can implement user handling in client. (ralam june 21, 2017)
sendAllUsersInMeeting(user.id)
sendAllVoiceUsersInMeeting(user.id, liveMeeting.voiceUsers, meetingId)
sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId)
val newState = userJoinMeeting(outGW, user.authToken, liveMeeting, state)
if (!Users2x.hasPresenter(liveMeeting.users2x)) {
automaticallyAssignPresenter(outGW, liveMeeting)
}
newState
}
def sendAllUsersInMeeting(requesterId: String): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId
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, waitingForAcceptance = u.waitingForAcceptance, emoji = u.emoji,
locked = u.locked, presenter = u.presenter, avatar = u.avatar)
}
val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers)
Sender.send(outGW, event)
}
def sendAllVoiceUsersInMeeting(requesterId: String, voiceUsers: VoiceUsers, meetingId: String): Unit = {
val vu = VoiceUsers.findAll(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 event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu)
Sender.send(outGW, event)
}
def notifyModeratorsOfGuestWaiting(guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = {
val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE)
mods foreach { m =>
val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests)
Sender.send(outGW, event)
}
}
}

View File

@ -1,11 +1,122 @@
package org.bigbluebutton.core.domain package org.bigbluebutton.core.domain
import com.softwaremill.quicklens._
import org.bigbluebutton.core.util.TimeUtil
case class MeetingInactivityTracker( case class MeetingInactivityTracker(
maxInactivityTimeoutMinutes: Int, val maxInactivityTimeoutMinutes: Int,
warningMinutesBeforeMax: Int, val warningMinutesBeforeMax: Int,
lastActivityTimeInMinutes: Long, lastActivityTimestamp: Long,
warningSent: Boolean, warningSent: Boolean,
warningSentOnTimeInMinutes: Long warningSentOnTimestamp: Long
) )
case class MeetingExpiryTracker(startedOnInMinutes: Long, meetingJoined: Boolean, lastUserLeftOn: Long) object MeetingInactivityTracker {
def warningHasBeenSent(state: MeetingState2x): Boolean = {
state.inactivityTracker.warningSent
}
def setWarningSentAndTimestamp(state: MeetingState2x, nowInSeconds: Long): MeetingState2x = {
val tracker = state.inactivityTracker.modify(_.warningSent).setTo(true)
.modify(_.warningSentOnTimestamp).setTo(nowInSeconds)
state.modify(_.inactivityTracker).setTo(tracker)
}
def resetWarningSentAndTimestamp(state: MeetingState2x): MeetingState2x = {
val tracker = state.inactivityTracker.modify(_.warningSent).setTo(false)
.modify(_.warningSentOnTimestamp).setTo(0L)
state.modify(_.inactivityTracker).setTo(tracker)
}
def updateLastActivityTimestamp(state: MeetingState2x, nowInSeconds: Long): MeetingState2x = {
val tracker = state.inactivityTracker.modify(_.lastActivityTimestamp).setTo(nowInSeconds)
state.modify(_.inactivityTracker).setTo(tracker)
}
def hasRecentActivity(state: MeetingState2x, nowInSeconds: Long): Boolean = {
nowInSeconds - state.inactivityTracker.lastActivityTimestamp <
TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes) -
TimeUtil.minutesToSeconds(state.inactivityTracker.warningMinutesBeforeMax)
}
def isMeetingInactive(state: MeetingState2x, nowInSeconds: Long): Boolean = {
state.inactivityTracker.warningSent &&
(nowInSeconds - state.inactivityTracker.lastActivityTimestamp) >
TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes)
}
def timeLeftInSeconds(state: MeetingState2x, nowInSeconds: Long): Long = {
state.inactivityTracker.lastActivityTimestamp +
TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes) - nowInSeconds
}
}
case class MeetingExpiryTracker(
startedOn: Long,
userHasJoined: Boolean,
lastUserLeftOn: Option[Long],
durationInMinutes: Int,
meetingExpireIfNoUserJoinedInMinutes: Int,
meetingExpireWhenLastUserLeftInMinutes: Int
)
object MeetingExpiryTracker {
def setMeetingStartedOn(state: MeetingState2x, timestampInSeconds: Long): MeetingState2x = {
val tracker = state.expiryTracker.modify(_.startedOn).setTo(timestampInSeconds)
state.modify(_.expiryTracker).setTo(tracker)
}
def setUserHasJoined(state: MeetingState2x): MeetingState2x = {
val tracker = state.expiryTracker.modify(_.userHasJoined).setTo(true)
.modify(_.lastUserLeftOn).setTo(None)
state.modify(_.expiryTracker).setTo(tracker)
}
def hasMeetingExpiredAfterLastUserLeft(state: MeetingState2x, timestampInSeconds: Long): Boolean = {
val expire = for {
lastUserLeftOn <- state.expiryTracker.lastUserLeftOn
} yield {
timestampInSeconds - lastUserLeftOn >
TimeUtil.minutesToSeconds(state.expiryTracker.meetingExpireWhenLastUserLeftInMinutes)
}
expire.getOrElse(false)
}
def setLastUserLeftOn(state: MeetingState2x, timestampInSeconds: Long): MeetingState2x = {
val tracker = state.expiryTracker.modify(_.lastUserLeftOn).setTo(Some(timestampInSeconds))
state.modify(_.expiryTracker).setTo(tracker)
}
def hasMeetingExpired(state: MeetingState2x, timestampInSeconds: Long): (Boolean, Option[String]) = {
if (MeetingExpiryTracker.hasMeetingExpiredNeverBeenJoined(state, timestampInSeconds)) {
(true, Some(MeetingEndReason.ENDED_WHEN_NOT_JOINED))
} else if (MeetingExpiryTracker.meetingOverDuration(state, timestampInSeconds)) {
(true, Some(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION))
} else if (MeetingExpiryTracker.hasMeetingExpiredAfterLastUserLeft(state, timestampInSeconds)) {
(true, Some(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT))
} else {
(false, None)
}
}
def hasMeetingExpiredNeverBeenJoined(state: MeetingState2x, nowInSeconds: Long): Boolean = {
!state.expiryTracker.userHasJoined &&
(nowInSeconds - state.expiryTracker.startedOn >
TimeUtil.minutesToSeconds(state.expiryTracker.meetingExpireIfNoUserJoinedInMinutes))
}
def meetingOverDuration(state: MeetingState2x, nowInSeconds: Long): Boolean = {
if (state.expiryTracker.durationInMinutes == 0) {
false
} else {
nowInSeconds > state.expiryTracker.startedOn + TimeUtil.minutesToSeconds(state.expiryTracker.durationInMinutes)
}
}
def endMeetingTime(state: MeetingState2x): Int = {
(state.expiryTracker.startedOn + TimeUtil.minutesToSeconds(state.expiryTracker.durationInMinutes)).toInt
}
}

View File

@ -0,0 +1,19 @@
package org.bigbluebutton.core.domain
object MeetingState2x {
}
case class MeetingState2x(
inactivityTracker: MeetingInactivityTracker,
expiryTracker: MeetingExpiryTracker
)
object MeetingEndReason {
val ENDED_FROM_API = "ENDED_FROM_API"
val ENDED_DUE_TO_INACTIVITY = "ENDED_DUE_TO_ACTIVITY"
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_USER_LOGGED_OUT"
}

View File

@ -6,10 +6,8 @@ import com.softwaremill.quicklens._
object RegisteredUsers { object RegisteredUsers {
def create(userId: String, extId: String, name: String, roles: String, def create(userId: String, extId: String, name: String, roles: String,
token: String, avatar: String, guest: Boolean, authenticated: Boolean, token: String, avatar: String, guest: Boolean, authenticated: Boolean,
waitingForAcceptance: Boolean, users: RegisteredUsers): RegisteredUser = { waitingForAcceptance: Boolean): RegisteredUser = {
val ru = new RegisteredUser(userId, extId, name, roles, token, avatar, guest, authenticated, waitingForAcceptance) new RegisteredUser(userId, extId, name, roles, token, avatar, guest, authenticated, waitingForAcceptance)
users.save(ru)
ru
} }
def findWithToken(token: String, users: RegisteredUsers): Option[RegisteredUser] = { def findWithToken(token: String, users: RegisteredUsers): Option[RegisteredUser] = {
@ -43,6 +41,10 @@ object RegisteredUsers {
} yield users.save(regUser) } yield users.save(regUser)
} }
def add(users: RegisteredUsers, user: RegisteredUser): Vector[RegisteredUser] = {
users.save(user)
}
def remove(id: String, users: RegisteredUsers): Option[RegisteredUser] = { def remove(id: String, users: RegisteredUsers): Option[RegisteredUser] = {
users.delete(id) users.delete(id)
} }

View File

@ -71,6 +71,16 @@ class ReceivedJsonMsgHandlerActor(
case DestroyMeetingSysCmdMsg.NAME => case DestroyMeetingSysCmdMsg.NAME =>
route[DestroyMeetingSysCmdMsg](meetingManagerChannel, envelope, jsonNode) route[DestroyMeetingSysCmdMsg](meetingManagerChannel, envelope, jsonNode)
// Guests
case GetGuestsWaitingApprovalReqMsg.NAME =>
routeGenericMsg[GetGuestsWaitingApprovalReqMsg](envelope, jsonNode)
case GuestsWaitingApprovedMsg.NAME =>
routeGenericMsg[GuestsWaitingApprovedMsg](envelope, jsonNode)
case SetGuestPolicyCmdMsg.NAME =>
routeGenericMsg[SetGuestPolicyCmdMsg](envelope, jsonNode)
case GetGuestPolicyReqMsg.NAME =>
routeGenericMsg[GetGuestPolicyReqMsg](envelope, jsonNode)
// Users // Users
case GetUsersMeetingReqMsg.NAME => case GetUsersMeetingReqMsg.NAME =>
routeGenericMsg[GetUsersMeetingReqMsg](envelope, jsonNode) routeGenericMsg[GetUsersMeetingReqMsg](envelope, jsonNode)

View File

@ -1,168 +1,192 @@
package org.bigbluebutton.core.running package org.bigbluebutton.core.running
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.api.RecordingStatusChanged import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.{ MessageRecorder, OutMessageGateway } import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged }
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus }
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder } import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.models._
trait HandlerHelpers { import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder }
def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String,
valid: Boolean, waitForApproval: Boolean): Unit = { trait HandlerHelpers extends SystemConfiguration {
val event = MsgBuilder.buildValidateAuthTokenRespMsg(
meetingId, def sendAllWebcamStreams(outGW: OutMessageGateway, requesterId: String, webcams: Webcams, meetingId: String): Unit = {
userId, authToken, valid, waitForApproval val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams)
) val webcamStreams = streams.map { u =>
Sender.send(outGW, event) val msVO = MediaStreamVO(id = u.stream.id, url = u.stream.url, userId = u.stream.userId,
attributes = u.stream.attributes, viewers = u.stream.viewers)
// TODO: Should disconnect user here.
} WebcamStreamVO(streamId = msVO.id, stream = msVO)
}
def sendValidateAuthTokenRespMsg(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String,
valid: Boolean, waitForApproval: Boolean): Unit = { val event = MsgBuilder.buildGetWebcamStreamsMeetingRespMsg(meetingId, requesterId, webcamStreams)
val event = MsgBuilder.buildValidateAuthTokenRespMsg( Sender.send(outGW, event)
meetingId, }
userId, authToken, valid, waitForApproval
) def userJoinMeeting(outGW: OutMessageGateway, authToken: String,
Sender.send(outGW, event) liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
} val nu = for {
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers)
def userValidatedButNeedToWaitForApproval(outGW: OutMessageGateway, liveMeeting: LiveMeeting, } yield {
user: RegisteredUser): Unit = { UserState(
val meetingId = liveMeeting.props.meetingProp.intId intId = regUser.id,
sendValidateAuthTokenRespMsg(outGW, meetingId, user.id, user.authToken, valid = true, waitForApproval = false) extId = regUser.externId,
name = regUser.name,
val guest = GuestWaiting(user.id, user.name, user.role) role = regUser.role,
addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) guest = regUser.guest,
notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId) authed = regUser.authed,
} waitingForAcceptance = regUser.waitingForAcceptance,
emoji = "none",
def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = { presenter = false,
GuestsWaiting.add(guestsWaitingList, guest) locked = false,
} avatar = regUser.avatarURL
)
def userValidatedAndNoNeedToWaitForApproval( }
outGW: OutMessageGateway,
liveMeeting: LiveMeeting, nu match {
user: RegisteredUser case Some(newUser) =>
): Unit = { Users2x.add(liveMeeting.users2x, newUser)
println("**************** userValidatedAndNoNeedToWaitForApproval") val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser)
Sender.send(outGW, event)
val meetingId = liveMeeting.props.meetingProp.intId startRecordingIfAutoStart2x(liveMeeting)
sendValidateAuthTokenRespMsg(outGW, meetingId,
userId = user.id, authToken = user.authToken, valid = true, waitForApproval = false) if (!state.expiryTracker.userHasJoined) {
MeetingExpiryTracker.setUserHasJoined(state)
// TODO: REMOVE Temp only so we can implement user handling in client. (ralam june 21, 2017) } else {
state
sendAllUsersInMeeting(outGW, user.id, liveMeeting) }
sendAllVoiceUsersInMeeting(outGW, user.id, liveMeeting.voiceUsers, meetingId)
sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId) case None =>
userJoinMeeting(outGW, user.authToken, liveMeeting) state
if (!Users2x.hasPresenter(liveMeeting.users2x)) { }
automaticallyAssignPresenter(outGW, liveMeeting) }
}
} def startRecordingIfAutoStart2x(liveMeeting: LiveMeeting): Unit = {
if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
def notifyModeratorsOfGuestWaiting(outGW: OutMessageGateway, guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE)
mods foreach { m => MeetingStatus2x.recordingStarted(liveMeeting.status)
val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) // outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record,
Sender.send(outGW, event) // "system", MeetingStatus2x.isRecording(liveMeeting.status)))
} }
} }
def sendAllUsersInMeeting(outGW: OutMessageGateway, requesterId: String, liveMeeting: LiveMeeting): Unit = { def automaticallyAssignPresenter(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId val meetingId = liveMeeting.props.meetingProp.intId
val users = Users2x.findAll(liveMeeting.users2x) for {
val webUsers = users.map { u => moderator <- Users2x.findModerator(liveMeeting.users2x)
WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role, newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId)
guest = u.guest, authed = u.authed, waitingForAcceptance = u.waitingForAcceptance, emoji = u.emoji, } yield {
locked = u.locked, presenter = u.presenter, avatar = u.avatar) sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.name)
} }
}
val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers)
Sender.send(outGW, event) def sendPresenterAssigned(outGW: OutMessageGateway, meetingId: String, intId: String, name: String, assignedBy: String): Unit = {
} def event = MsgBuilder.buildPresenterAssignedEvtMsg(meetingId, intId, name, assignedBy)
outGW.send(event)
def sendAllWebcamStreams(outGW: OutMessageGateway, requesterId: String, webcams: Webcams, meetingId: String): Unit = { }
val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams)
val webcamStreams = streams.map { u => def endMeeting(outGW: OutMessageGateway, liveMeeting: LiveMeeting, reason: String): Unit = {
val msVO = MediaStreamVO(id = u.stream.id, url = u.stream.url, userId = u.stream.userId, def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
attributes = u.stream.attributes, viewers = u.stream.viewers) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing)
WebcamStreamVO(streamId = msVO.id, stream = msVO) val body = MeetingEndingEvtMsgBody(meetingId, reason)
} val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used")
val event = MeetingEndingEvtMsg(header, body)
val event = MsgBuilder.buildGetWebcamStreamsMeetingRespMsg(meetingId, requesterId, webcamStreams)
Sender.send(outGW, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def sendAllVoiceUsersInMeeting(outGW: OutMessageGateway, requesterId: String, val endingEvent = buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId)
voiceUsers: VoiceUsers,
meetingId: String): Unit = { // Broadcast users the meeting will end
outGW.send(endingEvent)
val vu = VoiceUsers.findAll(voiceUsers).map { u =>
VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName, MeetingStatus2x.meetingHasEnded(liveMeeting.status)
callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
} def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu) val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing)
Sender.send(outGW, event) val body = MeetingEndedEvtMsgBody(meetingId)
} val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME)
val event = MeetingEndedEvtMsg(header, body)
def userJoinMeeting(outGW: OutMessageGateway, authToken: String, liveMeeting: LiveMeeting): Unit = {
for { BbbCommonEnvCoreMsg(envelope, event)
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers) }
} yield {
val userState = UserState( val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId)
intId = regUser.id, outGW.send(endedEvnt)
extId = regUser.externId, }
name = regUser.name,
role = regUser.role, def destroyMeeting(eventBus: IncomingEventBus, meetingId: String): Unit = {
guest = regUser.guest, eventBus.publish(BigBlueButtonEvent(meetingManagerChannel, new DestroyMeetingInternalMsg(meetingId)))
authed = regUser.authed, }
waitingForAcceptance = regUser.waitingForAcceptance,
emoji = "none", def notifyParentThatBreakoutEnded(eventBus: IncomingEventBus, liveMeeting: LiveMeeting): Unit = {
presenter = false, if (liveMeeting.props.meetingProp.isBreakout) {
locked = false, eventBus.publish(BigBlueButtonEvent(
avatar = regUser.avatarURL liveMeeting.props.breakoutProps.parentId,
) new BreakoutRoomEndedInternalMsg(liveMeeting.props.meetingProp.intId)
))
Users2x.add(liveMeeting.users2x, userState) }
}
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, userState)
Sender.send(outGW, event) def ejectAllUsersFromVoiceConf(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
val event = MsgBuilder.buildEjectAllFromVoiceConfMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf)
MessageRecorder.record(outGW, liveMeeting.props.recordProp.record, event.core) outGW.send(event)
startRecordingIfAutoStart2x(liveMeeting) }
}
} def sendEndMeetingDueToExpiry(reason: String, eventBus: IncomingEventBus, outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
endMeeting(outGW, liveMeeting, reason)
def startRecordingIfAutoStart2x(liveMeeting: LiveMeeting): Unit = { notifyParentThatBreakoutEnded(eventBus, liveMeeting)
if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) && ejectAllUsersFromVoiceConf(outGW, liveMeeting)
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) { destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId)
}
MeetingStatus2x.recordingStarted(liveMeeting.status)
// outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record, def sendEndMeetingDueToExpiry2(reason: String, eventBus: IncomingEventBus, outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
// "system", MeetingStatus2x.isRecording(liveMeeting.status))) val meetingId = liveMeeting.props.meetingProp.intId
}
} val endMeetingEvt = buildMeetingEndingEvtMsg(reason, meetingId)
outGW.send(endMeetingEvt)
def automaticallyAssignPresenter(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId val endedEvt = buildMeetingEndedEvtMsg(meetingId)
for { outGW.send(endedEvt)
moderator <- Users2x.findModerator(liveMeeting.users2x)
newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId) if (liveMeeting.props.meetingProp.isBreakout) {
} yield { eventBus.publish(BigBlueButtonEvent(
sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.name) liveMeeting.props.breakoutProps.parentId,
} new BreakoutRoomEndedInternalMsg(meetingId)
} ))
}
def sendPresenterAssigned(outGW: OutMessageGateway, meetingId: String, intId: String, name: String, assignedBy: String): Unit = {
def event = MsgBuilder.buildPresenterAssignedEvtMsg(meetingId, intId, name, assignedBy) val event = MsgBuilder.buildEjectAllFromVoiceConfMsg(meetingId, liveMeeting.props.voiceProp.voiceConf)
outGW.send(event) outGW.send(event)
}
} eventBus.publish(BigBlueButtonEvent(meetingManagerChannel, new DestroyMeetingInternalMsg(meetingId)))
MeetingStatus2x.meetingHasEnded(liveMeeting.status)
}
def buildMeetingEndingEvtMsg(reason: String, meetingId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing)
val body = MeetingEndingEvtMsgBody(meetingId, reason)
val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used")
val event = MeetingEndingEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing)
val body = MeetingEndedEvtMsgBody(meetingId)
val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME)
val event = MeetingEndedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}

View File

@ -1,11 +1,8 @@
package org.bigbluebutton.core.running package org.bigbluebutton.core.running
import java.util.concurrent.TimeUnit
import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.api._ import org.bigbluebutton.core.api._
import org.bigbluebutton.core.apps._ import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.domain.Meeting3x
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.MeetingStatus2x
@ -29,32 +26,6 @@ class LiveMeeting(
val guestsWaiting: GuestsWaiting val guestsWaiting: GuestsWaiting
) { ) {
def hasMeetingEnded(): Boolean = {
MeetingStatus2x.hasMeetingEnded(status)
}
def startCheckingIfWeNeedToEndVoiceConf() {
if (Users2x.numUsers(users2x) == 0 && !props.meetingProp.isBreakout) {
MeetingStatus2x.lastWebUserLeft(status)
}
}
def sendTimeRemainingNotice() {
val now = timeNowInSeconds
if (props.durationProps.duration > 0 && (((MeetingStatus2x.startedOn(status) + props.durationProps.duration) - now) < 15)) {
// log.warning("MEETING WILL END IN 15 MINUTES!!!!")
}
}
def timeNowInMinutes(): Long = {
TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
}
def timeNowInSeconds(): Long = {
TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
}
def lockLayout(lock: Boolean) { def lockLayout(lock: Boolean) {
MeetingStatus2x.lockLayout(status, lock) MeetingStatus2x.lockLayout(status, lock)
} }

View File

@ -1,434 +1,394 @@
package org.bigbluebutton.core.running package org.bigbluebutton.core.running
import java.io.{ PrintWriter, StringWriter } import java.io.{ PrintWriter, StringWriter }
import org.bigbluebutton.core.apps.users.UsersApp import org.bigbluebutton.core.apps.users._
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker } import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x }
import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core.util.TimeUtil
//import java.util.concurrent.TimeUnit //import java.util.concurrent.TimeUnit
import akka.actor._ import akka.actor._
import akka.actor.SupervisorStrategy.Resume import akka.actor.SupervisorStrategy.Resume
import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core._ import org.bigbluebutton.core._
import org.bigbluebutton.core.api._ import org.bigbluebutton.core.api._
import org.bigbluebutton.core.apps._ import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.apps.caption.CaptionApp2x import org.bigbluebutton.core.apps.caption.CaptionApp2x
import org.bigbluebutton.core.apps.chat.ChatApp2x import org.bigbluebutton.core.apps.chat.ChatApp2x
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
import org.bigbluebutton.core.apps.presentation.PresentationApp2x import org.bigbluebutton.core.apps.presentation.PresentationApp2x
import org.bigbluebutton.core.apps.meeting._ import org.bigbluebutton.core.apps.meeting._
import org.bigbluebutton.core.apps.users.UsersApp2x import org.bigbluebutton.core.apps.users.UsersApp2x
import org.bigbluebutton.core.apps.sharednotes.SharedNotesApp2x import org.bigbluebutton.core.apps.sharednotes.SharedNotesApp2x
import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x
import org.bigbluebutton.core.bus._ import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.handlers._ import org.bigbluebutton.core2.message.handlers._
import org.bigbluebutton.core2.message.handlers.users._ import org.bigbluebutton.core2.message.handlers.users._
import org.bigbluebutton.core2.message.handlers.meeting._ import org.bigbluebutton.core2.message.handlers.meeting._
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.breakout._ import org.bigbluebutton.core.apps.breakout._
import org.bigbluebutton.core.apps.polls._ import org.bigbluebutton.core.apps.polls._
import org.bigbluebutton.core.apps.voice._ import org.bigbluebutton.core.apps.voice._
import com.softwaremill.quicklens._
import scala.concurrent.duration._ import scala.concurrent.duration._
import org.bigbluebutton.core2.testdata.FakeTestData import org.bigbluebutton.core2.testdata.FakeTestData
import org.bigbluebutton.core.apps.layout.LayoutApp2x import org.bigbluebutton.core.apps.layout.LayoutApp2x
import org.bigbluebutton.core.apps.meeting.SyncGetMeetingInfoRespMsgHdlr import org.bigbluebutton.core.apps.meeting.SyncGetMeetingInfoRespMsgHdlr
object MeetingActor { object MeetingActor {
def props( def props(
props: DefaultProps, props: DefaultProps,
eventBus: IncomingEventBus, eventBus: IncomingEventBus,
outGW: OutMessageGateway, outGW: OutMessageGateway,
liveMeeting: LiveMeeting liveMeeting: LiveMeeting
): Props = ): Props =
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting) Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
} }
class MeetingActor( class MeetingActor(
val props: DefaultProps, val props: DefaultProps,
val eventBus: IncomingEventBus, val eventBus: IncomingEventBus,
val outGW: OutMessageGateway, val outGW: OutMessageGateway,
val liveMeeting: LiveMeeting val liveMeeting: LiveMeeting
) )
extends BaseMeetingActor extends BaseMeetingActor
with GuestsApp with GuestsApp
with LayoutApp2x with LayoutApp2x
with VoiceApp2x with VoiceApp2x
with PollApp2x with PollApp2x
with BreakoutApp2x with BreakoutApp2x
with UsersApp2x with UsersApp2x
with WhiteboardApp2x with WhiteboardApp2x
with PermisssionCheck with PermisssionCheck
with UserBroadcastCamStartMsgHdlr with UserBroadcastCamStartMsgHdlr
with UserJoinMeetingReqMsgHdlr with UserJoinMeetingReqMsgHdlr
with UserBroadcastCamStopMsgHdlr with UserBroadcastCamStopMsgHdlr
with UserConnectedToGlobalAudioMsgHdlr with UserConnectedToGlobalAudioMsgHdlr
with UserDisconnectedFromGlobalAudioMsgHdlr with UserDisconnectedFromGlobalAudioMsgHdlr
with MuteAllExceptPresentersCmdMsgHdlr with MuteAllExceptPresentersCmdMsgHdlr
with MuteMeetingCmdMsgHdlr with MuteMeetingCmdMsgHdlr
with IsMeetingMutedReqMsgHdlr with IsMeetingMutedReqMsgHdlr
with MuteUserCmdMsgHdlr with MuteUserCmdMsgHdlr
with EjectUserFromVoiceCmdMsgHdlr with EjectUserFromVoiceCmdMsgHdlr
with EndMeetingSysCmdMsgHdlr with EndMeetingSysCmdMsgHdlr
with DestroyMeetingSysCmdMsgHdlr with DestroyMeetingSysCmdMsgHdlr
with SendTimeRemainingUpdateHdlr with SendTimeRemainingUpdateHdlr
with SyncGetMeetingInfoRespMsgHdlr { with SyncGetMeetingInfoRespMsgHdlr {
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case e: Exception => { case e: Exception => {
val sw: StringWriter = new StringWriter() val sw: StringWriter = new StringWriter()
sw.write("An exception has been thrown on MeetingActor, exception message [" + e.getMessage() + "] (full stacktrace below)\n") sw.write("An exception has been thrown on MeetingActor, exception message [" + e.getMessage() + "] (full stacktrace below)\n")
e.printStackTrace(new PrintWriter(sw)) e.printStackTrace(new PrintWriter(sw))
log.error(sw.toString()) log.error(sw.toString())
Resume Resume
} }
} }
/** /**
* Put the internal message injector into another actor so this * Put the internal message injector into another actor so this
* actor is easy to test. * actor is easy to test.
*/ */
var actorMonitor = context.actorOf( var actorMonitor = context.actorOf(
MeetingActorAudit.props(props, eventBus, outGW), MeetingActorAudit.props(props, eventBus, outGW),
"actorMonitor-" + props.meetingProp.intId "actorMonitor-" + props.meetingProp.intId
) )
val presentationApp2x = new PresentationApp2x(liveMeeting, outGW) val presentationApp2x = new PresentationApp2x(liveMeeting, outGW)
val screenshareApp2x = new ScreenshareApp2x(liveMeeting, outGW) val screenshareApp2x = new ScreenshareApp2x(liveMeeting, outGW)
val captionApp2x = new CaptionApp2x(liveMeeting, outGW) val captionApp2x = new CaptionApp2x(liveMeeting, outGW)
val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW) val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW)
val chatApp2x = new ChatApp2x(liveMeeting, outGW) val chatApp2x = new ChatApp2x(liveMeeting, outGW)
val usersApp = new UsersApp(liveMeeting, outGW) val usersApp = new UsersApp(liveMeeting, outGW, eventBus)
var inactivityTracker = new MeetingInactivityTracker( val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW, eventBus)
liveMeeting.props.durationProps.maxInactivityTimeoutMinutes,
liveMeeting.props.durationProps.warnMinutesBeforeMax, val inactivityTracker = new MeetingInactivityTracker(
TimeUtil.millisToMinutes(System.currentTimeMillis()), false, 0L props.durationProps.maxInactivityTimeoutMinutes,
) props.durationProps.warnMinutesBeforeMax,
lastActivityTimestamp = TimeUtil.timeNowInSeconds(),
var expiryTracker = new MeetingExpiryTracker( warningSent = false,
TimeUtil.millisToMinutes(System.currentTimeMillis()), warningSentOnTimestamp = 0L
false, 0L )
)
val expiryTracker = new MeetingExpiryTracker(
/*******************************************************************/ startedOn = TimeUtil.timeNowInSeconds(),
//object FakeTestData extends FakeTestData userHasJoined = false,
//FakeTestData.createFakeUsers(liveMeeting) lastUserLeftOn = None,
/*******************************************************************/ durationInMinutes = props.durationProps.duration,
meetingExpireIfNoUserJoinedInMinutes = props.durationProps.meetingExpireIfNoUserJoinedInMinutes,
def receive = { meetingExpireWhenLastUserLeftInMinutes = props.durationProps.meetingExpireWhenLastUserLeftInMinutes
//============================= )
// 2x messages
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg) var state = new MeetingState2x(inactivityTracker, expiryTracker)
// Handling RegisterUserReqMsg as it is forwarded from BBBActor and /*******************************************************************/
// its type is not BbbCommonEnvCoreMsg //object FakeTestData extends FakeTestData
case m: RegisterUserReqMsg => usersApp.handleRegisterUserReqMsg(m) //FakeTestData.createFakeUsers(liveMeeting)
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m) /*******************************************************************/
// Meeting def receive = {
case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m) //=============================
// 2x messages
//====================================== case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
//======================================= // Handling RegisterUserReqMsg as it is forwarded from BBBActor and
// old messages // its type is not BbbCommonEnvCoreMsg
case msg: MonitorNumberOfUsers => handleMonitorNumberOfUsers(msg) case m: RegisterUserReqMsg => usersApp.handleRegisterUserReqMsg(m)
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg)
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg) // Meeting
case msg: SendTimeRemainingUpdate => handleSendTimeRemainingUpdate(msg) case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m)
// Screenshare //======================================
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
//=======================================
// Guest // old messages
case msg: GetGuestPolicy => handleGetGuestPolicy(msg) case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg)
case msg: SetGuestPolicy => handleSetGuestPolicy(msg)
case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg)
case _ => // do nothing case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
} case msg: SendTimeRemainingUpdate => state = handleSendTimeRemainingUpdate(msg, state)
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = { // Screenshare
// TODO: Update meeting activity status here case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
// updateActivityStatus(msg)
case _ => // do nothing
msg.core match { }
// Users
case m: ValidateAuthTokenReqMsg => usersApp.handleValidateAuthTokenReqMsg(m) private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
case m: UserJoinMeetingReqMsg => handleUserJoinMeetingReqMsg(m) state = MeetingInactivityTracker.updateLastActivityTimestamp(state, TimeUtil.timeNowInSeconds())
case m: UserLeaveReqMsg => handleUserLeaveReqMsg(m)
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m) msg.core match {
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m) // Users
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m) case m: ValidateAuthTokenReqMsg =>
case m: MeetingActivityResponseCmdMsg => inactivityTracker = usersApp.handleMeetingActivityResponseCmdMsg(m, inactivityTracker) state = usersApp.handleValidateAuthTokenReqMsg(m, state)
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m) case m: UserJoinMeetingReqMsg =>
case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m) state = handleUserJoinMeetingReqMsg(m, state)
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m) case m: UserLeaveReqMsg =>
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m) state = handleUserLeaveReqMsg(m, state)
case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m) case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m) case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
// Whiteboard case m: MeetingActivityResponseCmdMsg =>
case m: SendCursorPositionPubMsg => handleSendCursorPositionPubMsg(m) state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
case m: ClearWhiteboardPubMsg => handleClearWhiteboardPubMsg(m) case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m)
case m: UndoWhiteboardPubMsg => handleUndoWhiteboardPubMsg(m) case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m)
case m: ModifyWhiteboardAccessPubMsg => handleModifyWhiteboardAccessPubMsg(m) case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
case m: GetWhiteboardAccessReqMsg => handleGetWhiteboardAccessReqMsg(m) case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
case m: SendWhiteboardAnnotationPubMsg => handleSendWhiteboardAnnotationPubMsg(m) case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m)
case m: GetWhiteboardAnnotationsReqMsg => handleGetWhiteboardAnnotationsReqMsg(m) case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m)
// Poll // Whiteboard
case m: StartPollReqMsg => handleStartPollReqMsg(m) case m: SendCursorPositionPubMsg => handleSendCursorPositionPubMsg(m)
case m: StartCustomPollReqMsg => handleStartCustomPollReqMsg(m) case m: ClearWhiteboardPubMsg => handleClearWhiteboardPubMsg(m)
case m: StopPollReqMsg => handleStopPollReqMsg(m) case m: UndoWhiteboardPubMsg => handleUndoWhiteboardPubMsg(m)
case m: ShowPollResultReqMsg => handleShowPollResultReqMsg(m) case m: ModifyWhiteboardAccessPubMsg => handleModifyWhiteboardAccessPubMsg(m)
case m: HidePollResultReqMsg => handleHidePollResultReqMsg(m) case m: GetWhiteboardAccessReqMsg => handleGetWhiteboardAccessReqMsg(m)
case m: GetCurrentPollReqMsg => handleGetCurrentPollReqMsg(m) case m: SendWhiteboardAnnotationPubMsg => handleSendWhiteboardAnnotationPubMsg(m)
case m: RespondToPollReqMsg => handleRespondToPollReqMsg(m) case m: GetWhiteboardAnnotationsReqMsg => handleGetWhiteboardAnnotationsReqMsg(m)
// Breakout // Poll
case m: BreakoutRoomsListMsg => handleBreakoutRoomsListMsg(m) case m: StartPollReqMsg => handleStartPollReqMsg(m)
case m: CreateBreakoutRoomsCmdMsg => handleCreateBreakoutRoomsCmdMsg(m) case m: StartCustomPollReqMsg => handleStartCustomPollReqMsg(m)
case m: EndAllBreakoutRoomsMsg => handleEndAllBreakoutRoomsMsg(m) case m: StopPollReqMsg => handleStopPollReqMsg(m)
case m: RequestBreakoutJoinURLReqMsg => handleRequestBreakoutJoinURLReqMsg(m) case m: ShowPollResultReqMsg => handleShowPollResultReqMsg(m)
case m: BreakoutRoomCreatedMsg => handleBreakoutRoomCreatedMsg(m) case m: HidePollResultReqMsg => handleHidePollResultReqMsg(m)
case m: BreakoutRoomEndedMsg => handleBreakoutRoomEndedMsg(m) case m: GetCurrentPollReqMsg => handleGetCurrentPollReqMsg(m)
case m: BreakoutRoomUsersUpdateMsg => handleBreakoutRoomUsersUpdateMsg(m) case m: RespondToPollReqMsg => handleRespondToPollReqMsg(m)
case m: SendBreakoutUsersUpdateMsg => handleSendBreakoutUsersUpdateMsg(m)
case m: TransferUserToMeetingRequestMsg => handleTransferUserToMeetingRequestMsg(m) // Breakout
case m: BreakoutRoomsListMsg => handleBreakoutRoomsListMsg(m)
// Voice case m: CreateBreakoutRoomsCmdMsg => handleCreateBreakoutRoomsCmdMsg(m)
case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m) case m: EndAllBreakoutRoomsMsg => handleEndAllBreakoutRoomsMsg(m)
case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m) case m: RequestBreakoutJoinURLReqMsg => handleRequestBreakoutJoinURLReqMsg(m)
case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m) case m: BreakoutRoomCreatedMsg => handleBreakoutRoomCreatedMsg(m)
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m) case m: BreakoutRoomEndedMsg => handleBreakoutRoomEndedMsg(m)
case m: MuteUserCmdMsg => handleMuteUserCmdMsg(m) case m: BreakoutRoomUsersUpdateMsg => handleBreakoutRoomUsersUpdateMsg(m)
case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m) case m: SendBreakoutUsersUpdateMsg => handleSendBreakoutUsersUpdateMsg(m)
case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m) case m: TransferUserToMeetingRequestMsg => handleTransferUserToMeetingRequestMsg(m)
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m) // Voice
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m) case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m)
case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m) case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m)
case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m)
// Layout case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m) case m: MuteUserCmdMsg => handleMuteUserCmdMsg(m)
case m: LockLayoutMsg => handleLockLayoutMsg(m) case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m)
case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m) case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m)
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
// Presentation case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m)
case m: SetCurrentPresentationPubMsg => presentationApp2x.handleSetCurrentPresentationPubMsg(m) case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m)
case m: GetPresentationInfoReqMsg => presentationApp2x.handleGetPresentationInfoReqMsg(m) case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m)
case m: SetCurrentPagePubMsg => presentationApp2x.handleSetCurrentPagePubMsg(m)
case m: ResizeAndMovePagePubMsg => presentationApp2x.handleResizeAndMovePagePubMsg(m) // Layout
case m: RemovePresentationPubMsg => presentationApp2x.handleRemovePresentationPubMsg(m) case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handlePreuploadedPresentationsPubMsg(m) case m: LockLayoutMsg => handleLockLayoutMsg(m)
case m: PresentationConversionUpdateSysPubMsg => presentationApp2x.handlePresentationConversionUpdatePubMsg(m) case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m)
case m: PresentationPageCountErrorSysPubMsg => presentationApp2x.handlePresentationPageCountErrorPubMsg(m)
case m: PresentationPageGeneratedSysPubMsg => presentationApp2x.handlePresentationPageGeneratedPubMsg(m) // Presentation
case m: PresentationConversionCompletedSysPubMsg => presentationApp2x.handlePresentationConversionCompletedPubMsg(m) case m: SetCurrentPresentationPubMsg => presentationApp2x.handleSetCurrentPresentationPubMsg(m)
case m: AssignPresenterReqMsg => handlePresenterChange(m) case m: GetPresentationInfoReqMsg => presentationApp2x.handleGetPresentationInfoReqMsg(m)
case m: SetCurrentPagePubMsg => presentationApp2x.handleSetCurrentPagePubMsg(m)
// Caption case m: ResizeAndMovePagePubMsg => presentationApp2x.handleResizeAndMovePagePubMsg(m)
case m: EditCaptionHistoryPubMsg => captionApp2x.handleEditCaptionHistoryPubMsg(m) case m: RemovePresentationPubMsg => presentationApp2x.handleRemovePresentationPubMsg(m)
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handleUpdateCaptionOwnerPubMsg(m) case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handlePreuploadedPresentationsPubMsg(m)
case m: SendCaptionHistoryReqMsg => captionApp2x.handleSendCaptionHistoryReqMsg(m) case m: PresentationConversionUpdateSysPubMsg => presentationApp2x.handlePresentationConversionUpdatePubMsg(m)
case m: PresentationPageCountErrorSysPubMsg => presentationApp2x.handlePresentationPageCountErrorPubMsg(m)
// SharedNotes case m: PresentationPageGeneratedSysPubMsg => presentationApp2x.handlePresentationPageGeneratedPubMsg(m)
case m: GetSharedNotesPubMsg => sharedNotesApp2x.handleGetSharedNotesPubMsg(m) case m: PresentationConversionCompletedSysPubMsg => presentationApp2x.handlePresentationConversionCompletedPubMsg(m)
case m: SyncSharedNotePubMsg => sharedNotesApp2x.handleSyncSharedNotePubMsg(m) case m: AssignPresenterReqMsg => handlePresenterChange(m)
case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handleUpdateSharedNoteReqMsg(m)
case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handleCreateSharedNoteReqMsg(m) // Caption
case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handleDestroySharedNoteReqMsg(m) case m: EditCaptionHistoryPubMsg => captionApp2x.handleEditCaptionHistoryPubMsg(m)
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handleUpdateCaptionOwnerPubMsg(m)
// Guests case m: SendCaptionHistoryReqMsg => captionApp2x.handleSendCaptionHistoryReqMsg(m)
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
case m: SetGuestPolicyMsg => handleSetGuestPolicyMsg(m) // SharedNotes
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m) case m: GetSharedNotesPubMsg => sharedNotesApp2x.handleGetSharedNotesPubMsg(m)
case m: SyncSharedNotePubMsg => sharedNotesApp2x.handleSyncSharedNotePubMsg(m)
// Chat case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handleUpdateSharedNoteReqMsg(m)
case m: GetChatHistoryReqMsg => chatApp2x.handleGetChatHistoryReqMsg(m) case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handleCreateSharedNoteReqMsg(m)
case m: SendPublicMessagePubMsg => chatApp2x.handleSendPublicMessagePubMsg(m) case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handleDestroySharedNoteReqMsg(m)
case m: SendPrivateMessagePubMsg => chatApp2x.handleSendPrivateMessagePubMsg(m)
case m: ClearPublicChatHistoryPubMsg => chatApp2x.handleClearPublicChatHistoryPubMsg(m) // Guests
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
// Screenshare case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStartedVoiceConfEvtMsg(m) case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(m) case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(m) // Chat
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg(m) case m: GetChatHistoryReqMsg => chatApp2x.handleGetChatHistoryReqMsg(m)
case m: GetScreenshareStatusReqMsg => screenshareApp2x.handleGetScreenshareStatusReqMsg(m) case m: SendPublicMessagePubMsg => chatApp2x.handleSendPublicMessagePubMsg(m)
case m: SendPrivateMessagePubMsg => chatApp2x.handleSendPrivateMessagePubMsg(m)
case _ => log.warning("***** Cannot handle " + msg.envelope.name) case m: ClearPublicChatHistoryPubMsg => chatApp2x.handleClearPublicChatHistoryPubMsg(m)
}
} // Screenshare
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStartedVoiceConfEvtMsg(m)
def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = { case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(m)
// sync all meetings case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(m)
handleSyncGetMeetingInfoRespMsg(liveMeeting.props) case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg(m)
case m: GetScreenshareStatusReqMsg => screenshareApp2x.handleGetScreenshareStatusReqMsg(m)
// sync all users
usersApp.handleSyncGetUsersMeetingRespMsg() case _ => log.warning("***** Cannot handle " + msg.envelope.name)
}
// sync all presentations }
presentationApp2x.handleSyncGetPresentationInfoRespMsg()
def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = {
// TODO send all chat // sync all meetings
// TODO send all lock settings handleSyncGetMeetingInfoRespMsg(liveMeeting.props)
// TODO send all screen sharing info
} // sync all users
usersApp.handleSyncGetUsersMeetingRespMsg()
def handlePresenterChange(msg: AssignPresenterReqMsg): Unit = {
// Stop poll if one is running as presenter left // sync all presentations
handleStopPollReqMsg(msg.header.userId) presentationApp2x.handleSyncGetPresentationInfoRespMsg()
// switch user presenter status for old and new presenter // TODO send all chat
usersApp.handleAssignPresenterReqMsg(msg) // TODO send all lock settings
// TODO send all screen sharing info
// TODO stop current screen sharing session (initiated by the old presenter) }
} def handlePresenterChange(msg: AssignPresenterReqMsg): Unit = {
// Stop poll if one is running as presenter left
def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = { handleStopPollReqMsg(msg.header.userId)
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting=" // switch user presenter status for old and new presenter
+ ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel) + " URL:" + usersApp.handleAssignPresenterReqMsg(msg)
ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel))
// TODO stop current screen sharing session (initiated by the old presenter)
if (ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel)) {
// if the meeting has an ongoing WebRTC Deskshare session, send a notification }
//outGW.send(new DeskShareNotifyASingleViewer(props.meetingProp.intId, msg.requesterID,
// DeskshareModel.getRTMPBroadcastingUrl(liveMeeting.deskshareModel), def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
// DeskshareModel.getDesktopShareVideoWidth(liveMeeting.deskshareModel),
// DeskshareModel.getDesktopShareVideoHeight(liveMeeting.deskshareModel), true)) log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting="
} + ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel) + " URL:" +
} ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel))
def handleGetGuestPolicy(msg: GetGuestPolicy) { if (ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel)) {
// outGW.send(new GetGuestPolicyReply(msg.meetingID, props.recordProp.record, // if the meeting has an ongoing WebRTC Deskshare session, send a notification
// msg.requesterID, MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString())) //outGW.send(new DeskShareNotifyASingleViewer(props.meetingProp.intId, msg.requesterID,
} // DeskshareModel.getRTMPBroadcastingUrl(liveMeeting.deskshareModel),
// DeskshareModel.getDesktopShareVideoWidth(liveMeeting.deskshareModel),
def handleSetGuestPolicy(msg: SetGuestPolicy) { // DeskshareModel.getDesktopShareVideoHeight(liveMeeting.deskshareModel), true))
// MeetingStatus2x.setGuestPolicy(liveMeeting.status, msg.policy) }
// MeetingStatus2x.setGuestPolicySetBy(liveMeeting.status, msg.setBy) }
// outGW.send(new GuestPolicyChanged(msg.meetingID, props.recordProp.record,
// MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString())) def handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = {
} Users2x.findPresenter(liveMeeting.users2x) match {
case Some(curPres) => {
def handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = { val allowed = msg.userID equals (curPres.intId)
Users2x.findPresenter(liveMeeting.users2x) match { // outGW.send(AllowUserToShareDesktopOut(msg.meetingID, msg.userID, allowed))
case Some(curPres) => { }
val allowed = msg.userID equals (curPres.intId) case None => // do nothing
// outGW.send(AllowUserToShareDesktopOut(msg.meetingID, msg.userID, allowed)) }
} }
case None => // do nothing
} def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) {
} state = expiryTrackerHelper.processMeetingInactivityAudit(state)
state = expiryTrackerHelper.processMeetingExpiryAudit(state)
def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsers) { }
inactivityTracker = MeetingInactivityTrackerHelper.processMeetingInactivityAudit(
props = liveMeeting.props, def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
outGW,
eventBus, }
inactivityTracker
) def startRecordingIfAutoStart() {
if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
expiryTracker = MeetingExpiryTracker.processMeetingExpiryAudit(liveMeeting.props, expiryTracker, eventBus) props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
log.info("Auto start recording. meetingId={}", props.meetingProp.intId)
monitorNumberOfWebUsers() MeetingStatus2x.recordingStarted(liveMeeting.status)
monitorNumberOfUsers()
} def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
def monitorNumberOfWebUsers() { val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
def buildEjectAllFromVoiceConfMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = { val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val event = RecordingStatusChangedEvtMsg(header, body)
val envelope = BbbCoreEnvelope(EjectAllFromVoiceConfMsg.NAME, routing)
val body = EjectAllFromVoiceConfMsgBody(voiceConf) BbbCommonEnvCoreMsg(envelope, event)
val header = BbbCoreHeaderWithMeetingId(EjectAllFromVoiceConfMsg.NAME, meetingId) }
val event = EjectAllFromVoiceConfMsg(header, body)
val event = buildRecordingStatusChangedEvtMsg(
BbbCommonEnvCoreMsg(envelope, event) liveMeeting.props.meetingProp.intId,
} "system", MeetingStatus2x.isRecording(liveMeeting.status)
)
if (Users2x.numUsers(liveMeeting.users2x) == 0 && outGW.send(event)
MeetingStatus2x.lastWebUserLeftOn(liveMeeting.status) > 0) {
if (liveMeeting.timeNowInMinutes - MeetingStatus2x.lastWebUserLeftOn(liveMeeting.status) > 2) { }
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", props.meetingProp.intId) }
val event = buildEjectAllFromVoiceConfMsg(props.meetingProp.intId, props.voiceProp.voiceConf)
outGW.send(event) def stopAutoStartedRecording() {
} if (props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
} props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) {
} log.info("Last web user left. Auto stopping recording. meetingId={}", props.meetingProp.intId)
MeetingStatus2x.recordingStopped(liveMeeting.status)
def monitorNumberOfUsers() {
val hasUsers = Users2x.numUsers(liveMeeting.users2x) != 0 def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
// TODO: We could use a better control over this message to send it just when it really matters :) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, UpdateMeetingExpireMonitor(props.meetingProp.intId, hasUsers))) val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
} val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) { val event = RecordingStatusChangedEvtMsg(header, body)
} BbbCommonEnvCoreMsg(envelope, event)
}
def startRecordingIfAutoStart() {
if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) && val event = buildRecordingStatusChangedEvtMsg(
props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) { liveMeeting.props.meetingProp.intId,
log.info("Auto start recording. meetingId={}", props.meetingProp.intId) "system", MeetingStatus2x.isRecording(liveMeeting.status)
MeetingStatus2x.recordingStarted(liveMeeting.status) )
outGW.send(event)
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) def record(msg: BbbCoreMsg): Unit = {
val event = RecordingStatusChangedEvtMsg(header, body) if (liveMeeting.props.recordProp.record) {
outGW.record(msg)
BbbCommonEnvCoreMsg(envelope, event) }
} }
}
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
}
}
def stopAutoStartedRecording() {
if (props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) {
log.info("Last web user left. Auto stopping recording. meetingId={}", props.meetingProp.intId)
MeetingStatus2x.recordingStopped(liveMeeting.status)
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)
}
}
def record(msg: BbbCoreMsg): Unit = {
if (liveMeeting.props.recordProp.record) {
outGW.record(msg)
}
}
}

View File

@ -72,7 +72,7 @@ class MeetingActorAudit(
} }
def handleMonitorNumberOfWebUsers() { def handleMonitorNumberOfWebUsers() {
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsers(props.meetingProp.intId))) eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsersInternalMsg(props.meetingProp.intId)))
// Trigger updating users of time remaining on meeting. // Trigger updating users of time remaining on meeting.
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingUpdate(props.meetingProp.intId))) eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingUpdate(props.meetingProp.intId)))

View File

@ -1,47 +0,0 @@
package org.bigbluebutton.core.running
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.api.EndMeeting
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus }
import org.bigbluebutton.core.domain.MeetingExpiryTracker
import org.bigbluebutton.core.util.TimeUtil
object MeetingExpiryTracker {
def hasMeetingExpiredNeverBeenJoined(nowInMinutes: Long, startedOnInMinutes: Long, meetingExpireIfNoUserJoinedInMinutes: Long): Boolean = {
nowInMinutes - startedOnInMinutes > meetingExpireIfNoUserJoinedInMinutes
}
def meetingOverDuration(nowInMinutes: Long, startedOnInMinutes: Long, durationInMinutes: Long): Boolean = {
nowInMinutes > startedOnInMinutes + durationInMinutes
}
def processNeverBeenJoinedExpiry(nowInMinutes: Long, props: DefaultProps, tracker: MeetingExpiryTracker, eventBus: IncomingEventBus): MeetingExpiryTracker = {
if (hasMeetingExpiredNeverBeenJoined(nowInMinutes, tracker.startedOnInMinutes,
props.durationProps.meetingExpireIfNoUserJoinedInMinutes)) {
sendEndMeetingDueToExpiry(props, eventBus)
tracker
} else {
tracker
}
}
def processMeetingExpiryAudit(props: DefaultProps, tracker: MeetingExpiryTracker, eventBus: IncomingEventBus): MeetingExpiryTracker = {
val nowInMinutes = TimeUtil.millisToMinutes(System.currentTimeMillis())
if (!tracker.meetingJoined) {
processNeverBeenJoinedExpiry(nowInMinutes, props, tracker, eventBus)
} else {
if (meetingOverDuration(nowInMinutes, tracker.startedOnInMinutes, props.durationProps.duration)) {
sendEndMeetingDueToExpiry(props, eventBus)
tracker
} else {
tracker
}
}
}
def sendEndMeetingDueToExpiry(props: DefaultProps, eventBus: IncomingEventBus): Unit = {
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
}
}

View File

@ -0,0 +1,70 @@
package org.bigbluebutton.core.running
import akka.actor.ActorContext
import akka.event.Logging
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.bus.IncomingEventBus
import org.bigbluebutton.core.domain.{ MeetingEndReason, MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x }
import org.bigbluebutton.core.util.TimeUtil
class MeetingExpiryTrackerHelper(
val liveMeeting: LiveMeeting,
val outGW: OutMessageGateway,
val eventBus: IncomingEventBus
)(implicit val context: ActorContext) extends HandlerHelpers {
val log = Logging(context.system, getClass)
def processMeetingExpiryAudit(state: MeetingState2x): MeetingState2x = {
val nowInSeconds = TimeUtil.timeNowInSeconds()
val (expired, reason) = MeetingExpiryTracker.hasMeetingExpired(state, nowInSeconds)
if (expired) {
for {
expireReason <- reason
} yield {
sendEndMeetingDueToExpiry(expireReason, eventBus, outGW, liveMeeting)
}
}
state
}
def processMeetingInactivityAudit(state: MeetingState2x): MeetingState2x = {
val nowInSeconds = TimeUtil.timeNowInSeconds()
if (!MeetingInactivityTracker.hasRecentActivity(state, nowInSeconds)) {
if (MeetingInactivityTracker.isMeetingInactive(state, nowInSeconds)) {
sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_DUE_TO_INACTIVITY, eventBus, outGW, liveMeeting)
state
} else {
if (!MeetingInactivityTracker.warningHasBeenSent(state)) {
warnOfMeetingInactivity(nowInSeconds, state)
MeetingInactivityTracker.setWarningSentAndTimestamp(state, nowInSeconds)
} else {
state
}
}
} else {
state
}
}
def warnOfMeetingInactivity(nowInSeconds: Long, state: MeetingState2x): Unit = {
val timeLeftSeconds = MeetingInactivityTracker.timeLeftInSeconds(state, nowInSeconds)
val event = buildMeetingInactivityWarningEvtMsg(liveMeeting.props.meetingProp.intId, timeLeftSeconds)
outGW.send(event)
}
def buildMeetingInactivityWarningEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing)
val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec)
val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used")
val event = MeetingInactivityWarningEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}

View File

@ -1,112 +0,0 @@
package org.bigbluebutton.core.running
import org.bigbluebutton.core.domain.MeetingInactivityTracker
import com.softwaremill.quicklens._
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.api.EndMeeting
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus }
import org.bigbluebutton.core.util.TimeUtil
object MeetingInactivityTrackerHelper {
def isMeetingActive(
nowInMinutes: Long,
lastActivityTimeInMinutes: Long,
maxInactivityTimeoutMinutes: Long
): Boolean = {
nowInMinutes - lastActivityTimeInMinutes < maxInactivityTimeoutMinutes
}
def isMeetingInactive(
warningSent: Boolean,
nowInMinutes: Long,
lastActivityTimeInMinutes: Long,
maxInactivityTimeoutMinutes: Long
): Boolean = {
warningSent && (nowInMinutes - lastActivityTimeInMinutes) > maxInactivityTimeoutMinutes
}
def processMeetingInactivityAudit(
props: DefaultProps,
outGW: OutMessageGateway,
eventBus: IncomingEventBus,
tracker: MeetingInactivityTracker
): MeetingInactivityTracker = {
val nowInMinutes = TimeUtil.millisToMinutes(System.currentTimeMillis())
if (isMeetingActive(nowInMinutes, tracker.lastActivityTimeInMinutes, tracker.maxInactivityTimeoutMinutes)) {
tracker
} else {
if (isMeetingInactive(tracker.warningSent, nowInMinutes,
tracker.lastActivityTimeInMinutes,
tracker.maxInactivityTimeoutMinutes)) {
sendEndMeetingDueToInactivity(props, eventBus)
tracker
} else {
if (tracker.warningSent) {
tracker
} else {
warnOfMeetingInactivity(props, outGW, nowInMinutes, tracker)
tracker.modify(_.warningSent).setTo(true).modify(_.warningSentOnTimeInMinutes).setTo(nowInMinutes)
}
}
}
}
def timeLeftInMinutes(nowInMinutes: Long, tracker: MeetingInactivityTracker): Long = {
tracker.lastActivityTimeInMinutes + tracker.maxInactivityTimeoutMinutes - nowInMinutes
}
def warnOfMeetingInactivity(props: DefaultProps, outGW: OutMessageGateway,
nowInMinutes: Long, tracker: MeetingInactivityTracker): MeetingInactivityTracker = {
val timeLeftSeconds = TimeUtil.minutesToSeconds(timeLeftInMinutes(nowInMinutes, tracker))
sendMeetingInactivityWarning(props, outGW, timeLeftSeconds)
tracker
}
def sendEndMeetingDueToInactivity(props: DefaultProps, eventBus: IncomingEventBus): Unit = {
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
}
def sendMeetingInactivityWarning(props: DefaultProps, outGW: OutMessageGateway, timeLeftSeconds: Long): Unit = {
def build(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing)
val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec)
val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used")
val event = MeetingInactivityWarningEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = build(props.meetingProp.intId, timeLeftSeconds)
outGW.send(event)
}
def processMeetingActivityResponse(
props: DefaultProps,
outGW: OutMessageGateway,
msg: MeetingActivityResponseCmdMsg,
tracker: MeetingInactivityTracker
): MeetingInactivityTracker = {
def buildMeetingIsActiveEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingIsActiveEvtMsg.NAME, routing)
val body = MeetingIsActiveEvtMsgBody(meetingId)
val header = BbbClientMsgHeader(MeetingIsActiveEvtMsg.NAME, meetingId, "not-used")
val event = MeetingIsActiveEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildMeetingIsActiveEvtMsg(props.meetingProp.intId)
outGW.send(event)
tracker.modify(_.warningSent).setTo(false)
.modify(_.warningSentOnTimeInMinutes).setTo(0L)
.modify(_.lastActivityTimeInMinutes).setTo(System.currentTimeMillis())
}
}

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.running package org.bigbluebutton.core.running
import akka.actor.ActorContext import akka.actor.ActorContext
import org.bigbluebutton.common2.domain.{ DefaultProps } import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.apps._ import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.bus._ import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._

View File

@ -3,6 +3,7 @@ package org.bigbluebutton.core.util
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
object TimeUtil { object TimeUtil {
def minutesToMillis(minutes: Long): Long = { def minutesToMillis(minutes: Long): Long = {
TimeUnit.MINUTES.toMillis(minutes) TimeUnit.MINUTES.toMillis(minutes)
} }
@ -14,4 +15,7 @@ object TimeUtil {
def minutesToSeconds(minutes: Long): Long = { def minutesToSeconds(minutes: Long): Long = {
TimeUnit.MINUTES.toSeconds(minutes) TimeUnit.MINUTES.toSeconds(minutes)
} }
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
} }

View File

@ -39,9 +39,6 @@ object MeetingStatus2x {
def recordingStarted(status: MeetingStatus2x) = status.recording = true def recordingStarted(status: MeetingStatus2x) = status.recording = true
def recordingStopped(status: MeetingStatus2x) = status.recording = false def recordingStopped(status: MeetingStatus2x) = status.recording = false
def isRecording(status: MeetingStatus2x): Boolean = status.recording def isRecording(status: MeetingStatus2x): Boolean = status.recording
def lastWebUserLeft(status: MeetingStatus2x) = status.lastWebUserLeftOnTimestamp = MeetingStatus2x.timeNowInMinutes
def lastWebUserLeftOn(status: MeetingStatus2x): Long = status.lastWebUserLeftOnTimestamp
def resetLastWebUserLeftOn(status: MeetingStatus2x) = status.lastWebUserLeftOnTimestamp = 0
def setVoiceRecordingFilename(status: MeetingStatus2x, path: String) = status.voiceRecordingFilename = path def setVoiceRecordingFilename(status: MeetingStatus2x, path: String) = status.voiceRecordingFilename = path
def getVoiceRecordingFilename(status: MeetingStatus2x): String = status.voiceRecordingFilename def getVoiceRecordingFilename(status: MeetingStatus2x): String = status.voiceRecordingFilename
def permisionsInitialized(status: MeetingStatus2x): Boolean = status.permissionsInited def permisionsInitialized(status: MeetingStatus2x): Boolean = status.permissionsInited
@ -57,10 +54,6 @@ object MeetingStatus2x {
def timeNowInMinutes(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime()) def timeNowInMinutes(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
def timeNowInSeconds(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()) def timeNowInSeconds(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
def startedOn(status: MeetingStatus2x): Long = status.startedOn
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
} }
class MeetingStatus2x { class MeetingStatus2x {
@ -75,15 +68,10 @@ class MeetingStatus2x {
private var meetingEnded = false private var meetingEnded = false
private var meetingMuted = false private var meetingMuted = false
private var hasLastWebUserLeft = false
private var lastWebUserLeftOnTimestamp: Long = 0
private var voiceRecordingFilename: String = "" private var voiceRecordingFilename: String = ""
private var extension = new MeetingExtensionProp private var extension = new MeetingExtensionProp
private val startedOn = MeetingStatus2x.timeNowInSeconds;
private def startRecordingVoice() { private def startRecordingVoice() {
recordingVoice = true recordingVoice = true
} }
@ -115,9 +103,7 @@ class MeetingStatus2x {
private def recordingStarted() = recording = true private def recordingStarted() = recording = true
private def recordingStopped() = recording = false private def recordingStopped() = recording = false
private def isRecording(): Boolean = recording private def isRecording(): Boolean = recording
private def lastWebUserLeft() = lastWebUserLeftOnTimestamp = MeetingStatus2x.timeNowInMinutes
private def lastWebUserLeftOn(): Long = lastWebUserLeftOnTimestamp
private def resetLastWebUserLeftOn() = lastWebUserLeftOnTimestamp = 0
private def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path private def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path
private def getVoiceRecordingFilename(): String = voiceRecordingFilename private def getVoiceRecordingFilename(): String = voiceRecordingFilename
private def permisionsInitialized(): Boolean = permissionsInited private def permisionsInitialized(): Boolean = permissionsInited

View File

@ -2,10 +2,11 @@ package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.api.{ SendTimeRemainingUpdate } import org.bigbluebutton.core.api.SendTimeRemainingUpdate
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x }
import org.bigbluebutton.core.models.BreakoutRooms import org.bigbluebutton.core.models.BreakoutRooms
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core.util.TimeUtil
trait SendTimeRemainingUpdateHdlr { trait SendTimeRemainingUpdateHdlr {
this: BaseMeetingActor => this: BaseMeetingActor =>
@ -13,10 +14,11 @@ trait SendTimeRemainingUpdateHdlr {
val liveMeeting: LiveMeeting val liveMeeting: LiveMeeting
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate) { def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate, state: MeetingState2x): MeetingState2x = {
if (liveMeeting.props.durationProps.duration > 0) { if (liveMeeting.props.durationProps.duration > 0) {
val endMeetingTime = MeetingStatus2x.startedOn(liveMeeting.status) + (liveMeeting.props.durationProps.duration * 60) val endMeetingTime = MeetingExpiryTracker.endMeetingTime(state)
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds val timeRemaining = endMeetingTime - TimeUtil.timeNowInSeconds
def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
@ -34,7 +36,7 @@ trait SendTimeRemainingUpdateHdlr {
if (!liveMeeting.props.meetingProp.isBreakout && !BreakoutRooms.getRooms(liveMeeting.breakoutRooms).isEmpty) { if (!liveMeeting.props.meetingProp.isBreakout && !BreakoutRooms.getRooms(liveMeeting.breakoutRooms).isEmpty) {
val endMeetingTime = BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms) + val endMeetingTime = BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms) +
(BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms) * 60) (BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms) * 60)
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds val timeRemaining = endMeetingTime - TimeUtil.timeNowInSeconds
def buildBreakoutRoomsTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { def buildBreakoutRoomsTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
@ -53,6 +55,8 @@ trait SendTimeRemainingUpdateHdlr {
BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms, 0) BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms, 0)
BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms, 0) BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms, 0)
} }
state
} }
} }

View File

@ -0,0 +1,29 @@
package org.bigbluebutton.core2.message.handlers.guests
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.models.{ GuestWaiting, GuestsWaiting, Roles, Users2x }
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
trait GetGuestPolicyReqMsgHdlr {
this: BaseMeetingActor =>
val liveMeeting: LiveMeeting
val outGW: OutMessageGateway
def handleGetGuestPolicyReqMsg(msg: GetGuestPolicyReqMsg): Unit = {
val event = buildGetGuestPolicyRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy,
liveMeeting.guestsWaiting.getGuestPolicy().policy)
outGW.send(event)
}
def buildGetGuestPolicyRespMsg(meetingId: String, userId: String, policy: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetGuestPolicyRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetGuestPolicyRespMsg.NAME, meetingId, userId)
val body = GetGuestPolicyRespMsgBody(policy)
val event = GetGuestPolicyRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}

View File

@ -1,6 +1,6 @@
package org.bigbluebutton.core2.message.handlers.guests package org.bigbluebutton.core2.message.handlers.guests
import org.bigbluebutton.common2.msgs.SetGuestPolicyMsg import org.bigbluebutton.common2.msgs.{ SetGuestPolicyCmdMsg }
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.models.{ GuestPolicy, GuestPolicyType, GuestWaiting, GuestsWaiting } import org.bigbluebutton.core.models.{ GuestPolicy, GuestPolicyType, GuestWaiting, GuestsWaiting }
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting } import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting }
@ -12,7 +12,7 @@ trait SetGuestPolicyMsgHdlr {
val liveMeeting: LiveMeeting val liveMeeting: LiveMeeting
val outGW: OutMessageGateway val outGW: OutMessageGateway
def handleSetGuestPolicyMsg(msg: SetGuestPolicyMsg): Unit = { def handleSetGuestPolicyMsg(msg: SetGuestPolicyCmdMsg): Unit = {
val newPolicy = msg.body.policy.toUpperCase() val newPolicy = msg.body.policy.toUpperCase()
if (GuestPolicyType.policyTypes.contains(newPolicy)) { if (GuestPolicyType.policyTypes.contains(newPolicy)) {
val policy = GuestPolicy(newPolicy, msg.body.setBy) val policy = GuestPolicy(newPolicy, msg.body.setBy)

View File

@ -3,7 +3,6 @@ package org.bigbluebutton.core2.message.handlers.meeting
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.MsgBuilder import org.bigbluebutton.core2.message.senders.MsgBuilder
trait DestroyMeetingSysCmdMsgHdlr { trait DestroyMeetingSysCmdMsgHdlr {

View File

@ -2,24 +2,19 @@ package org.bigbluebutton.core2.message.handlers.meeting
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } import org.bigbluebutton.core.bus.IncomingEventBus
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core.domain.MeetingEndReason
import org.bigbluebutton.core2.message.senders.MsgBuilder import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting }
trait EndMeetingSysCmdMsgHdlr { trait EndMeetingSysCmdMsgHdlr extends HandlerHelpers {
this: BaseMeetingActor => this: BaseMeetingActor =>
val liveMeeting: LiveMeeting val liveMeeting: LiveMeeting
val outGW: OutMessageGateway val outGW: OutMessageGateway
val eventBus: IncomingEventBus
def handleEndMeeting(msg: EndMeetingSysCmdMsg) { def handleEndMeeting(msg: EndMeetingSysCmdMsg) {
// Broadcast users the meeting will end sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_FROM_API, eventBus, outGW, liveMeeting)
outGW.send(MsgBuilder.buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId))
MeetingStatus2x.meetingHasEnded(liveMeeting.status)
// Sent from akka-apps to bbb-web to inform about end of meeting
outGW.send(MsgBuilder.buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId))
} }
} }

View File

@ -1,18 +0,0 @@
package org.bigbluebutton.core2.message.handlers.users
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
import org.bigbluebutton.core.{ OutMessageGateway }
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, BaseMeetingActor }
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
this: BaseMeetingActor =>
val liveMeeting: LiveMeeting
val outGW: OutMessageGateway
def handleUserJoinMeetingReqMsg(msg: UserJoinMeetingReqMsg): Unit = {
userJoinMeeting(outGW, msg.body.authToken, liveMeeting)
}
}

View File

@ -1,49 +0,0 @@
package org.bigbluebutton.core2.message.handlers.users
import org.bigbluebutton.common2.domain.VoiceUserVO
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.MeetingStatus2x
trait UserJoiningHdlr {
this: MeetingActor =>
val outGW: OutMessageGateway
def handleUserJoin(msg: UserJoining): Unit = {
log.debug("Received user joined meeting. metingId=" + props.meetingProp.intId + " userId=" + msg.userID)
/*
for {
uvo <- Users.newUser(msg.userID, lockStatus, ru, waitingForAcceptance, vu, liveMeeting.users)
} yield {
log.info("User joined meeting. metingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
if (uvo.guest && MeetingStatus2x.getGuestPolicy(liveMeeting.status) == GuestPolicy.ALWAYS_DENY) {
outGW.send(new GuestAccessDenied(props.meetingProp.intId, props.recordProp.record, uvo.id))
} else {
outGW.send(new UserJoined(props.meetingProp.intId, props.recordProp.record, uvo))
outGW.send(new MeetingState(props.meetingProp.intId, props.recordProp.record, uvo.id,
MeetingStatus2x.getPermissions(liveMeeting.status), MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
if (!waitingForAcceptance) {
// Become presenter if the only moderator
if ((Users.numModerators(liveMeeting.users) == 1) || (Users.hasNoPresenter(liveMeeting.users))) {
if (ru.role == Roles.MODERATOR_ROLE) {
log.info("Assigning presenter to lone moderator. metingId=" + props.meetingProp.intId + " userId=" + uvo.id)
assignNewPresenter(msg.userID, ru.name, msg.userID)
}
}
} else {
log.info("User waiting for acceptance. metingId=" + props.meetingProp.intId + " userId=" + uvo.id)
}
liveMeeting.webUserJoined
startRecordingIfAutoStart()
}
}
*/
}
}

View File

@ -1,40 +0,0 @@
package org.bigbluebutton.core2.message.handlers.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.apps.users.UsersApp
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMessageGateway
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg): Unit = {
log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg)
RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) match {
case Some(u) =>
val guestPolicyType = GuestsWaiting.getGuestPolicy(liveMeeting.guestsWaiting).policy
if (guestPolicyType == GuestPolicyType.ALWAYS_ACCEPT) {
userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u)
} else if (guestPolicyType == GuestPolicyType.ASK_MODERATOR) {
if (u.guest && u.waitingForAcceptance) {
userValidatedButNeedToWaitForApproval(outGW, liveMeeting, u)
} else {
userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u)
}
} else {
validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId,
userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false)
// TODO: Disconnect user
}
case None =>
validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId,
userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false)
// TODO: Disconnect user
}
}
}

View File

@ -227,26 +227,6 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing)
val body = MeetingEndingEvtMsgBody(meetingId)
val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used")
val event = MeetingEndingEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing)
val body = MeetingEndedEvtMsgBody(meetingId)
val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME)
val event = MeetingEndedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildBreakoutRoomEndedEvtMsg(meetingId: String, userId: String, breakoutRoomId: String): BbbCommonEnvCoreMsg = { def buildBreakoutRoomEndedEvtMsg(meetingId: String, userId: String, breakoutRoomId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(BreakoutRoomEndedEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(BreakoutRoomEndedEvtMsg.NAME, routing)

View File

@ -9,14 +9,25 @@ import org.bigbluebutton.core.running.LiveMeeting
trait FakeTestData { trait FakeTestData {
def createFakeUsers(liveMeeting: LiveMeeting): Unit = { def createFakeUsers(liveMeeting: LiveMeeting): Unit = {
createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, false, false, CallingWith.WEBRTC, muted = false, val mod1 = createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, false, false, CallingWith.WEBRTC, muted = false,
talking = true, listenOnly = false) talking = true, listenOnly = false)
createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false, Users2x.add(liveMeeting.users2x, mod1)
val mod2 = createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, guest = false, authed = true, CallingWith.WEBRTC, muted = false,
talking = false, listenOnly = false) talking = false, listenOnly = false)
createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false, Users2x.add(liveMeeting.users2x, mod2)
val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false,
talking = false, listenOnly = false) talking = false, listenOnly = false)
createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false, Users2x.add(liveMeeting.users2x, guest1)
val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role)
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1)
val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false,
talking = false, listenOnly = false) talking = false, listenOnly = false)
Users2x.add(liveMeeting.users2x, guest2)
val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role)
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2)
val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false) val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
VoiceUsers.add(liveMeeting.voiceUsers, vu1) VoiceUsers.add(liveMeeting.voiceUsers, vu1)
@ -33,7 +44,7 @@ trait FakeTestData {
} }
def createUserVoiceAndCam(liveMeeting: LiveMeeting, role: String, guest: Boolean, authed: Boolean, callingWith: String, def createUserVoiceAndCam(liveMeeting: LiveMeeting, role: String, guest: Boolean, authed: Boolean, callingWith: String,
muted: Boolean, talking: Boolean, listenOnly: Boolean): Unit = { muted: Boolean, talking: Boolean, listenOnly: Boolean): UserState = {
val ruser1 = FakeUserGenerator.createFakeRegisteredUser(liveMeeting.registeredUsers, Roles.MODERATOR_ROLE, true, false) val ruser1 = FakeUserGenerator.createFakeRegisteredUser(liveMeeting.registeredUsers, Roles.MODERATOR_ROLE, true, false)
@ -49,10 +60,10 @@ trait FakeTestData {
createFakeUser(liveMeeting, ruser1) createFakeUser(liveMeeting, ruser1)
} }
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): Unit = { def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
guest = regUser.guest, authed = regUser.authed, waitingForAcceptance = regUser.waitingForAcceptance, guest = regUser.guest, authed = regUser.authed, waitingForAcceptance = regUser.waitingForAcceptance,
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL) emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL)
Users2x.add(liveMeeting.users2x, u)
} }
} }

View File

@ -53,8 +53,10 @@ object FakeUserGenerator {
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" + val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".png" RandomStringGenerator.randomAlphanumericString(10) + ".png"
RegisteredUsers.create(userId = id, extId, name, role, val ru = RegisteredUsers.create(userId = id, extId, name, role,
authToken, avatarURL, guest, authed, waitingForAcceptance = true, users) authToken, avatarURL, guest, authed, waitingForAcceptance = true)
RegisteredUsers.add(users, ru)
ru
} }
def createFakeVoiceUser(user: RegisteredUser, callingWith: String, muted: Boolean, talking: Boolean, def createFakeVoiceUser(user: RegisteredUser, callingWith: String, muted: Boolean, talking: Boolean,

View File

@ -9,12 +9,10 @@ case class DisconnectAllConnections(scope: String) extends SystemMessage
class MsgToClientGW(val connInvokerService: IConnectionInvokerService) { class MsgToClientGW(val connInvokerService: IConnectionInvokerService) {
def broadcastToMeeting(msg: BroadcastToMeetingMsg): Unit = { def broadcastToMeeting(msg: BroadcastToMeetingMsg): Unit = {
//println("**** MsgToClientGW broadcastToMeeting " + msg.json)
connInvokerService.sendMessage(msg) connInvokerService.sendMessage(msg)
} }
def directToClient(msg: DirectToClientMsg): Unit = { def directToClient(msg: DirectToClientMsg): Unit = {
//println("**** MsgToClientGW directToClient " + msg.json)
connInvokerService.sendMessage(msg) connInvokerService.sendMessage(msg)
} }

View File

@ -7,7 +7,7 @@ object GetGuestsWaitingApprovalReqMsg {
val NAME = "GetGuestsWaitingApprovalReqMsg" val NAME = "GetGuestsWaitingApprovalReqMsg"
} }
case class GetGuestsWaitingApprovalReqMsg(header: BbbClientMsgHeader, case class GetGuestsWaitingApprovalReqMsg(header: BbbClientMsgHeader,
body: GetGuestsWaitingApprovalReqMsgBody) extends BbbCoreMsg body: GetGuestsWaitingApprovalReqMsgBody) extends StandardMsg
case class GetGuestsWaitingApprovalReqMsgBody(requesterId: String) case class GetGuestsWaitingApprovalReqMsgBody(requesterId: String)
@ -47,7 +47,7 @@ object GuestsWaitingApprovedMsg {
} }
case class GuestsWaitingApprovedMsg(header: BbbClientMsgHeader, case class GuestsWaitingApprovedMsg(header: BbbClientMsgHeader,
body: GuestsWaitingApprovedMsgBody) extends BbbCoreMsg body: GuestsWaitingApprovedMsgBody) extends StandardMsg
case class GuestsWaitingApprovedMsgBody(guests: Vector[GuestApprovedVO], approvedBy: String) case class GuestsWaitingApprovedMsgBody(guests: Vector[GuestApprovedVO], approvedBy: String)
case class GuestApprovedVO(guest: String, approved: Boolean) case class GuestApprovedVO(guest: String, approved: Boolean)
@ -76,12 +76,12 @@ case class GuestApprovedEvtMsgBody(approved: Boolean, approvedBy: String)
/** /**
* Message from user to set the guest policy. * Message from user to set the guest policy.
*/ */
object SetGuestPolicyMsg { object SetGuestPolicyCmdMsg {
val NAME = "SetGuestPolicyMsg" val NAME = "SetGuestPolicyCmdMsg"
} }
case class SetGuestPolicyMsg(header: BbbClientMsgHeader, case class SetGuestPolicyCmdMsg(header: BbbClientMsgHeader,
body: SetGuestPolicyMsgBody) extends BbbCoreMsg body: SetGuestPolicyCmdMsgBody) extends StandardMsg
case class SetGuestPolicyMsgBody(policy: String, setBy: String) case class SetGuestPolicyCmdMsgBody(policy: String, setBy: String)
/** /**
* Message sent to all clients that guest policy has been changed. * Message sent to all clients that guest policy has been changed.
@ -94,6 +94,18 @@ case class GuestPolicyChangedEvtMsg(header: BbbClientMsgHeader,
case class GuestPolicyChangedEvtMsgBody(policy: String, setBy: String) case class GuestPolicyChangedEvtMsgBody(policy: String, setBy: String)
/**
* Message from user to get the guest policy.
*/
object GetGuestPolicyReqMsg { val NAME = "GetGuestPolicyReqMsg" }
case class GetGuestPolicyReqMsg(header: BbbClientMsgHeader,
body: GetGuestPolicyReqMsgBody) extends StandardMsg
case class GetGuestPolicyReqMsgBody(requestedBy: String)
/**
* Sent to client as response to query for guest policy.
*/
object GetGuestPolicyRespMsg { val NAME = "GetGuestPolicyRespMsg" }
case class GetGuestPolicyRespMsg(header: BbbClientMsgHeader,
body: GetGuestPolicyRespMsgBody) extends StandardMsg
case class GetGuestPolicyRespMsgBody(policy: String)

View File

@ -59,7 +59,7 @@ case class MeetingEndedEvtMsgBody(meetingId: String)
object MeetingEndingEvtMsg { val NAME = "MeetingEndingEvtMsg"} object MeetingEndingEvtMsg { val NAME = "MeetingEndingEvtMsg"}
case class MeetingEndingEvtMsg(header: BbbClientMsgHeader, case class MeetingEndingEvtMsg(header: BbbClientMsgHeader,
body: MeetingEndingEvtMsgBody) extends BbbCoreMsg body: MeetingEndingEvtMsgBody) extends BbbCoreMsg
case class MeetingEndingEvtMsgBody(meetingId: String) case class MeetingEndingEvtMsgBody(meetingId: String, reason: String)
/** /**

View File

@ -150,14 +150,14 @@ public class ConnectionInvokerService implements IConnectionInvokerService {
} }
private void handleCloseMeetingAllConnectionsMsg(CloseMeetingAllConnectionsMsg msg) { private void handleCloseMeetingAllConnectionsMsg(CloseMeetingAllConnectionsMsg msg) {
log.info("Disconnecting all clients for meeting {}", msg.meetingId);
IScope meetingScope = getScope(msg.meetingId); IScope meetingScope = getScope(msg.meetingId);
if (meetingScope != null) { if (meetingScope != null) {
Set<IConnection> conns = meetingScope.getClientConnections(); Set<IConnection> conns = meetingScope.getClientConnections();
for (IConnection conn : conns) { for (IConnection conn : conns) {
if (conn.isConnected()) { if (conn.isConnected()) {
String connId = (String) conn.getAttribute("INTERNAL_USER_ID");
log.info("Disconnecting client=[{}] from meeting=[{}]", connId, msg.meetingId);
conn.close(); conn.close();
} }
} }

View File

@ -13,5 +13,21 @@ package org.bigbluebutton.core.model
this.streamId = streamId; this.streamId = streamId;
this.userId = userId; this.userId = userId;
} }
public function addViewer(userId: String):void {
for (var i: int=0; i < viewers.length; i++) {
var viewer: String = viewers[i] as String;
if (viewer == userId) return;
}
viewers.push(userId);
}
public function removeViewer(userId: String):void {
var index: int = viewers.indexOf(userId);
if (index > -1 && index < viewers.length) {
viewers.removeAt(index);
}
}
} }
} }

View File

@ -85,7 +85,21 @@ package org.bigbluebutton.core.model
return tempArray; return tempArray;
} }
public function getStreamIdsViewingForUser(userId: String): Array { public function startedViewingStream(userId: String, streamId: String): void {
var _stream: MediaStream = getStream(streamId);
if (_stream != null) {
_stream.addViewer(userId);
}
}
public function stoppedViewingStream(userId: String, streamId: String): void {
var _stream: MediaStream = getStream(streamId);
if (_stream != null) {
_stream.removeViewer(userId);
}
}
public function getStreamIdsIAmViewingForUser(userId: String): Array {
var tempArray: Array = new Array(); var tempArray: Array = new Array();
for (var i:int = 0; i < _webcams.length; i++) { for (var i:int = 0; i < _webcams.length; i++) {

View File

@ -10,6 +10,10 @@ package org.bigbluebutton.core.model.users
private var _guests:ArrayCollection = new ArrayCollection(); private var _guests:ArrayCollection = new ArrayCollection();
public function getGuests(): Array {
return new ArrayCollection(_guests.toArray()).toArray();
}
public function add(user: GuestWaiting):void { public function add(user: GuestWaiting):void {
_guests.addItem(user); _guests.addItem(user);
} }
@ -63,6 +67,14 @@ package org.bigbluebutton.core.model.users
return -1; return -1;
} }
public function setGuestPolicy(value: String): void {
_guestPolicy = value;
}
public function getGuestPolicy(): String {
return _guestPolicy;
}
public function userJoined(vu: GuestWaiting):void { public function userJoined(vu: GuestWaiting):void {
add(vu); add(vu);
} }

View File

@ -18,62 +18,7 @@ package org.bigbluebutton.core.model.users
// Flag to tell that user is in the process of leaving the meeting. // Flag to tell that user is in the process of leaving the meeting.
public var isLeavingFlag:Boolean = false; public var isLeavingFlag:Boolean = false;
[Bindable] private var _viewingStream:Array = new Array();
[Bindable]
public function get viewingStream():Array {
return _viewingStream;
}
public function set viewingStream(v:Array):void {
throw new Error("Please use the helpers addViewingStream or removeViewingStream to handle viewingStream");
}
public function addViewingStream(streamName:String):Boolean {
if (isViewingStream(streamName)) {
return false;
}
_viewingStream.push(streamName);
return true;
}
public function removeViewingStream(streamName:String):Boolean {
if (!isViewingStream(streamName)) {
return false;
}
_viewingStream = _viewingStream.filter(function(item:*, index:int, array:Array):Boolean { return item != streamName; });
return true;
}
private function isViewingStream(streamName:String):Boolean {
return _viewingStream.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; });
}
public function isViewingAllStreams():Boolean {
return _viewingStream.length == streamNames.length;
}
[Bindable] public var streamNames:Array = new Array();
[Bindable]
public function get streamName():String {
var streams:String = "";
for each(var stream:String in streamNames) {
streams = streams + stream + "|";
}
//Remove last |
streams = streams.slice(0, streams.length-1);
return streams;
}
private function hasThisStream(streamName:String):Boolean {
return streamNames.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; });
}
public function set streamName(streamNames:String):void {
if(streamNames) {
var streamNamesList:Array = streamNames.split("|");
}
}
public static function copy(source: User2x):User2x { public static function copy(source: User2x):User2x {
var dest: User2x = new User2x(); var dest: User2x = new User2x();
dest.intId = source.intId; dest.intId = source.intId;

View File

@ -0,0 +1,38 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 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.main.events
{
import flash.events.Event;
public class StartedViewingWebcamEvent extends Event
{
public static const STARTED_VIEWING_WEBCAM:String = "started viewing webcam event";
// The userID of the webcam being viewed.
public var webcamUserID:String;
// The streamName of the user
public var streamName:String;
public function StartedViewingWebcamEvent(bubbles:Boolean=true, cancelable:Boolean=false)
{
super(STARTED_VIEWING_WEBCAM, bubbles, cancelable);
}
}
}

View File

@ -1,140 +1,146 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org BigBlueButton open source conferencing system - http://www.bigbluebutton.org
Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below). Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
BigBlueButton is free software; you can redistribute it and/or modify it under the 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 terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2.1 of the License, or (at your option) any later Foundation; either version 2.1 of the License, or (at your option) any later
version. version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY 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 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 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 You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$Id: $ $Id: $
--> -->
<mx:TitleWindow xmlns:mx="library://ns.adobe.com/flex/mx" <mx:TitleWindow xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:fx="http://ns.adobe.com/mxml/2009"
title="{ResourceUtil.getInstance().getString('bbb.guests.title')}" showCloseButton="false" creationComplete="init()" title="{ResourceUtil.getInstance().getString('bbb.guests.title')}" showCloseButton="false" creationComplete="init()"
x="0" y="0" layout="vertical" width="320" horizontalAlign="center" x="0" y="0" layout="vertical" width="320" horizontalAlign="center"
xmlns:mate="http://mate.asfusion.com/" > xmlns:mate="http://mate.asfusion.com/" >
<fx:Declarations> <fx:Declarations>
<mate:Listener type="{BBBEvent.ACCEPT_ALL_WAITING_GUESTS}" method="acceptAllWaitingGuests" /> <mate:Listener type="{BBBEvent.ACCEPT_ALL_WAITING_GUESTS}" method="acceptAllWaitingGuests" />
<mate:Listener type="{BBBEvent.DENY_ALL_WAITING_GUESTS}" method="denyAllWaitingGuests" /> <mate:Listener type="{BBBEvent.DENY_ALL_WAITING_GUESTS}" method="denyAllWaitingGuests" />
<mate:Listener type="{RemoveGuestFromViewEvent.REMOVE_GUEST}" receive="{remove(event.userid)}" /> <mate:Listener type="{RemoveGuestFromViewEvent.REMOVE_GUEST}" receive="{remove(event.userid)}" />
</fx:Declarations> </fx:Declarations>
<fx:Script> <fx:Script>
<![CDATA[ <![CDATA[
import mx.managers.PopUpManager; import com.asfusion.mate.events.Dispatcher;
import com.asfusion.mate.events.Dispatcher;
import org.bigbluebutton.main.events.RemoveGuestEvent; import mx.controls.Button;
import org.bigbluebutton.main.events.ResponseModeratorEvent; import mx.events.CloseEvent;
import org.bigbluebutton.main.events.RemoveGuestFromViewEvent; import mx.managers.PopUpManager;
import org.bigbluebutton.util.i18n.ResourceUtil;
import mx.controls.Button; import org.bigbluebutton.core.model.LiveMeeting;
import mx.events.CloseEvent; import org.bigbluebutton.core.model.users.GuestWaiting;
import org.bigbluebutton.main.events.BBBEvent; import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.RemoveGuestEvent;
private var guestButtons:Object = new Object(); import org.bigbluebutton.main.events.RemoveGuestFromViewEvent;
[Bindable] private var numberOfGuests:Number = 0; import org.bigbluebutton.main.events.ResponseModeratorEvent;
private var dispatcher:Dispatcher = new Dispatcher(); import org.bigbluebutton.util.i18n.ResourceUtil;
public function init():void { private var guestButtons:Object = new Object();
//Uncomment this line to make titlewindow undraggable [Bindable] private var numberOfGuests:Number = 0;
//this.isPopUp = false; private var dispatcher:Dispatcher = new Dispatcher();
}
public function init():void {
public function refreshGuestView(listOfGuests:Object):void { //Uncomment this line to make titlewindow undraggable
for (var userid:String in listOfGuests) { //this.isPopUp = false;
if(guestButtons[userid] == null) { }
var guestItem:GuestItem = new GuestItem();
guestItem.setUser(listOfGuests[userid], userid); public function refreshGuestView():void {
guestListBox.addChild(guestItem); var _guests: Array = LiveMeeting.inst().guestsWaiting.getGuests();
guestButtons[userid] = guestItem; for (var i:int = 0; i < _guests.length; i++) {
numberOfGuests++; var _guest: GuestWaiting = _guests[i] as GuestWaiting;
} if(guestButtons[_guest.intId] == null) {
} var guestItem:GuestItem = new GuestItem();
this.visible = true; guestItem.setUser(_guest.name, _guest.intId);
} guestListBox.addChild(guestItem);
guestButtons[_guest.intId] = guestItem;
public function sendResponseToAllGuests(resp:Boolean):void { numberOfGuests++;
removeAllGuests(); }
var respCommand:ResponseModeratorEvent = new ResponseModeratorEvent(ResponseModeratorEvent.RESPONSE_ALL); }
respCommand.resp = resp; this.visible = true;
dispatcher.dispatchEvent(respCommand); }
}
public function sendResponseToAllGuests(resp:Boolean):void {
public function sendResponseToAllGuestsCheckBox(resp:Boolean):void { removeAllGuests();
if(rememberCheckBox.selected) { var respCommand:ResponseModeratorEvent = new ResponseModeratorEvent(ResponseModeratorEvent.RESPONSE_ALL);
var event:BBBEvent = new BBBEvent(BBBEvent.BROADCAST_GUEST_POLICY); respCommand.resp = resp;
if (resp) { dispatcher.dispatchEvent(respCommand);
event.payload['guestPolicy'] = "ALWAYS_ACCEPT"; }
} else {
event.payload['guestPolicy'] = "ALWAYS_DENY"; public function sendResponseToAllGuestsCheckBox(resp:Boolean):void {
} if(rememberCheckBox.selected) {
dispatcher.dispatchEvent(event); var event:BBBEvent = new BBBEvent(BBBEvent.BROADCAST_GUEST_POLICY);
} if (resp) {
sendResponseToAllGuests(resp); event.payload['guestPolicy'] = "ALWAYS_ACCEPT";
} } else {
event.payload['guestPolicy'] = "ALWAYS_DENY";
public function acceptAllWaitingGuests(event:BBBEvent):void { }
sendResponseToAllGuests(true); dispatcher.dispatchEvent(event);
} }
sendResponseToAllGuests(resp);
public function denyAllWaitingGuests(event:BBBEvent):void { }
sendResponseToAllGuests(false);
} public function acceptAllWaitingGuests(event:BBBEvent):void {
sendResponseToAllGuests(true);
public function removeAllGuests():void { }
var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent(RemoveGuestEvent.REMOVE_ALL);
dispatcher.dispatchEvent(removeGuestEvent); public function denyAllWaitingGuests(event:BBBEvent):void {
sendResponseToAllGuests(false);
closeWindow(); }
}
public function removeAllGuests():void {
public function remove(userid:String):void { var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent(RemoveGuestEvent.REMOVE_ALL);
if (guestButtons[userid] != null) { dispatcher.dispatchEvent(removeGuestEvent);
numberOfGuests = numberOfGuests - 1;
guestListBox.removeChild(guestButtons[userid]); closeWindow();
delete guestButtons[userid]; }
var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent(); public function remove(userid:String):void {
removeGuestEvent.userid = userid; if (guestButtons[userid] != null) {
dispatcher.dispatchEvent(removeGuestEvent); numberOfGuests = numberOfGuests - 1;
guestListBox.removeChild(guestButtons[userid]);
if (!hasGuest()) { delete guestButtons[userid];
closeWindow();
} var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent();
} removeGuestEvent.userid = userid;
} dispatcher.dispatchEvent(removeGuestEvent);
public function hasGuest():Boolean { if (!hasGuest()) {
return numberOfGuests > 0; closeWindow();
} }
}
public function closeWindow():void { }
this.visible = false;
PopUpManager.removePopUp(this); public function hasGuest():Boolean {
dispatchEvent(new CloseEvent(CloseEvent.CLOSE)); return numberOfGuests > 0;
} }
]]> public function closeWindow():void {
</fx:Script> this.visible = false;
<mx:Label text="{numberOfGuests > 1? ResourceUtil.getInstance().getString('bbb.guests.message.plural', [String(numberOfGuests)]): ResourceUtil.getInstance().getString('bbb.guests.message.singular', [String(numberOfGuests)])}"/> PopUpManager.removePopUp(this);
<mx:HRule width="100%"/> //dispatchEvent(new CloseEvent(CloseEvent.CLOSE));
<mx:Button id="allowEveryoneBtn" label="{ResourceUtil.getInstance().getString('bbb.guests.allowEveryoneBtn.text')}" width="70%" click="sendResponseToAllGuestsCheckBox(true)" toolTip="{allowEveryoneBtn.label}"/> }
<mx:Button id="denyEveryoneBtn" label="{ResourceUtil.getInstance().getString('bbb.guests.denyEveryoneBtn.text')}" width="70%" click="sendResponseToAllGuestsCheckBox(false)" toolTip="{denyEveryoneBtn.label}"/>
<mx:CheckBox id="rememberCheckBox" label="{ResourceUtil.getInstance().getString('bbb.guests.rememberAction.text')}"/> ]]>
<mx:HRule width="100%"/> </fx:Script>
<mx:VBox id="guestListBox" width="100%" height="100%" maxHeight="200" paddingLeft="10" paddingRight="10" paddingBottom="2" /> <mx:Label text="{numberOfGuests > 1? ResourceUtil.getInstance().getString('bbb.guests.message.plural', [String(numberOfGuests)]): ResourceUtil.getInstance().getString('bbb.guests.message.singular', [String(numberOfGuests)])}"/>
<mx:HRule width="100%"/>
<mx:Button id="allowEveryoneBtn" label="{ResourceUtil.getInstance().getString('bbb.guests.allowEveryoneBtn.text')}" width="70%" click="sendResponseToAllGuestsCheckBox(true)" toolTip="{allowEveryoneBtn.label}"/>
<mx:Button id="denyEveryoneBtn" label="{ResourceUtil.getInstance().getString('bbb.guests.denyEveryoneBtn.text')}" width="70%" click="sendResponseToAllGuestsCheckBox(false)" toolTip="{denyEveryoneBtn.label}"/>
<mx:CheckBox id="rememberCheckBox" label="{ResourceUtil.getInstance().getString('bbb.guests.rememberAction.text')}"/>
<mx:HRule width="100%"/>
<mx:VBox id="guestListBox" width="100%" height="100%" maxHeight="200" paddingLeft="10" paddingRight="10" paddingBottom="2" />
</mx:TitleWindow> </mx:TitleWindow>

View File

@ -78,75 +78,75 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mate:Listener type="{BBBEvent.RETRIEVE_GUEST_POLICY}" method="setGuestPolicy"/> <mate:Listener type="{BBBEvent.RETRIEVE_GUEST_POLICY}" method="setGuestPolicy"/>
<mate:Listener type="{ConnectionFailedEvent.MODERATOR_DENIED_ME}" method="handleLogout" /> <mate:Listener type="{ConnectionFailedEvent.MODERATOR_DENIED_ME}" method="handleLogout" />
<mate:Listener type="{BBBEvent.MODERATOR_ALLOWED_ME_TO_JOIN}" method="guestAllowed" /> <mate:Listener type="{BBBEvent.MODERATOR_ALLOWED_ME_TO_JOIN}" method="guestAllowed" />
<mate:Listener type="{RefreshGuestEvent.REFRESH_GUEST_VIEW}" method="refreshGuestView" /> <mate:Listener type="{NewGuestWaitingEvent.NEW_GUEST_WAITING}" method="refreshGuestView" />
<mate:Listener type="{BBBEvent.REMOVE_GUEST_FROM_LIST}" method="removeGuestWindow" /> <mate:Listener type="{BBBEvent.REMOVE_GUEST_FROM_LIST}" method="removeGuestWindow" />
<mate:Listener type="{BBBEvent.WAITING_FOR_MODERATOR_ACCEPTANCE}" method="openWaitWindow" /> <mate:Listener type="{BBBEvent.WAITING_FOR_MODERATOR_ACCEPTANCE}" method="openWaitWindow" />
<mate:Listener type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}" method="closeWaitWindow"/> <mate:Listener type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}" method="closeWaitWindow"/>
</fx:Declarations> </fx:Declarations>
<fx:Script> <fx:Script>
<![CDATA[ <![CDATA[
import com.asfusion.mate.events.Dispatcher; import com.asfusion.mate.events.Dispatcher;
import flash.events.Event; import flash.events.Event;
import flash.events.FullScreenEvent; import flash.events.FullScreenEvent;
import flash.events.IOErrorEvent; import flash.events.IOErrorEvent;
import flash.events.TextEvent; import flash.events.TextEvent;
import flash.geom.Point; import flash.geom.Point;
import flash.net.navigateToURL; import flash.net.navigateToURL;
import mx.binding.utils.ChangeWatcher; import mx.binding.utils.ChangeWatcher;
import mx.collections.ArrayCollection; import mx.collections.ArrayCollection;
import mx.controls.Alert; import mx.controls.Alert;
import mx.core.FlexGlobals; import mx.core.FlexGlobals;
import mx.core.IFlexDisplayObject; import mx.core.IFlexDisplayObject;
import mx.core.UIComponent; import mx.core.UIComponent;
import mx.events.FlexEvent; import mx.events.FlexEvent;
import flexlib.mdi.effects.effectsLib.MDIVistaEffects; import flexlib.mdi.effects.effectsLib.MDIVistaEffects;
import org.as3commons.lang.StringUtils; import org.as3commons.lang.StringUtils;
import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger; import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow; import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.events.AddUIComponentToMainCanvas; import org.bigbluebutton.common.events.AddUIComponentToMainCanvas;
import org.bigbluebutton.common.events.CloseWindowEvent; import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent; import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.common.events.ToolbarButtonEvent; import org.bigbluebutton.common.events.ToolbarButtonEvent;
import org.bigbluebutton.core.BBB; import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.Options; import org.bigbluebutton.core.Options;
import org.bigbluebutton.core.PopUpUtil; import org.bigbluebutton.core.PopUpUtil;
import org.bigbluebutton.core.UsersUtil; import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.LockControlEvent; import org.bigbluebutton.core.events.LockControlEvent;
import org.bigbluebutton.core.events.SwitchedLayoutEvent; import org.bigbluebutton.core.events.NewGuestWaitingEvent;
import org.bigbluebutton.core.vo.LockSettingsVO; import org.bigbluebutton.core.events.SwitchedLayoutEvent;
import org.bigbluebutton.main.events.AppVersionEvent; import org.bigbluebutton.core.vo.LockSettingsVO;
import org.bigbluebutton.main.events.BBBEvent; import org.bigbluebutton.main.events.AppVersionEvent;
import org.bigbluebutton.main.events.BreakoutRoomEvent; import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.ClientStatusEvent; import org.bigbluebutton.main.events.BreakoutRoomEvent;
import org.bigbluebutton.main.events.ConfigLoadedEvent; import org.bigbluebutton.main.events.ClientStatusEvent;
import org.bigbluebutton.main.events.ExitApplicationEvent; import org.bigbluebutton.main.events.ConfigLoadedEvent;
import org.bigbluebutton.main.events.InvalidAuthTokenEvent; import org.bigbluebutton.main.events.ExitApplicationEvent;
import org.bigbluebutton.main.events.MeetingNotFoundEvent; import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
import org.bigbluebutton.main.events.ModuleLoadEvent; import org.bigbluebutton.main.events.MeetingNotFoundEvent;
import org.bigbluebutton.main.events.NetworkStatsEvent; import org.bigbluebutton.main.events.ModuleLoadEvent;
import org.bigbluebutton.main.events.RefreshGuestEvent; import org.bigbluebutton.main.events.NetworkStatsEvent;
import org.bigbluebutton.main.events.ShortcutEvent; import org.bigbluebutton.main.events.ShortcutEvent;
import org.bigbluebutton.main.model.Guest; import org.bigbluebutton.main.model.Guest;
import org.bigbluebutton.main.model.ImageLoader; import org.bigbluebutton.main.model.ImageLoader;
import org.bigbluebutton.main.model.options.BrandingOptions; import org.bigbluebutton.main.model.options.BrandingOptions;
import org.bigbluebutton.main.model.options.BrowserVersionsOptions; import org.bigbluebutton.main.model.options.BrowserVersionsOptions;
import org.bigbluebutton.main.model.options.LanguageOptions; import org.bigbluebutton.main.model.options.LanguageOptions;
import org.bigbluebutton.main.model.options.LayoutOptions; import org.bigbluebutton.main.model.options.LayoutOptions;
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent; import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
import org.bigbluebutton.modules.phone.events.AudioSelectionWindowEvent; import org.bigbluebutton.modules.phone.events.AudioSelectionWindowEvent;
import org.bigbluebutton.modules.phone.events.FlashMicSettingsEvent; import org.bigbluebutton.modules.phone.events.FlashMicSettingsEvent;
import org.bigbluebutton.modules.phone.events.WebRTCCallEvent; import org.bigbluebutton.modules.phone.events.WebRTCCallEvent;
import org.bigbluebutton.modules.phone.events.WebRTCEchoTestEvent; import org.bigbluebutton.modules.phone.events.WebRTCEchoTestEvent;
import org.bigbluebutton.modules.phone.events.WebRTCMediaEvent; import org.bigbluebutton.modules.phone.events.WebRTCMediaEvent;
import org.bigbluebutton.modules.phone.models.PhoneOptions; import org.bigbluebutton.modules.phone.models.PhoneOptions;
import org.bigbluebutton.modules.users.views.BreakoutRoomSettings; import org.bigbluebutton.modules.users.views.BreakoutRoomSettings;
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent; import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
import org.bigbluebutton.util.i18n.ResourceUtil; import org.bigbluebutton.util.i18n.ResourceUtil;
private static const LOGGER:ILogger = getClassLogger(MainApplicationShell); private static const LOGGER:ILogger = getClassLogger(MainApplicationShell);
@ -493,7 +493,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
} }
} }
private function refreshGuestView(evt:RefreshGuestEvent):void { private function refreshGuestView(evt:NewGuestWaitingEvent):void {
// do not show the guest window if the user isn't moderator or if he's waiting for acceptance // do not show the guest window if the user isn't moderator or if he's waiting for acceptance
if (!UsersUtil.amIModerator() || UsersUtil.amIWaitingForAcceptance()) { if (!UsersUtil.amIModerator() || UsersUtil.amIWaitingForAcceptance()) {
closeGuestWindow(); closeGuestWindow();
@ -507,7 +507,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
guestWindow.x = systemManager.screen.width - guestWindow.width - 20; guestWindow.x = systemManager.screen.width - guestWindow.width - 20;
guestWindow.y = 20; guestWindow.y = 20;
} }
guestWindow.refreshGuestView(evt.listOfGuests); guestWindow.refreshGuestView();
} }
public function removeGuestWindow(evt:BBBEvent):void { public function removeGuestWindow(evt:BBBEvent):void {

View File

@ -24,16 +24,12 @@ package org.bigbluebutton.modules.users.events
{ {
public static const VIEW_CAMERA_EVENT:String = "VIEW_CAMERA_EVENT"; public static const VIEW_CAMERA_EVENT:String = "VIEW_CAMERA_EVENT";
public var stream:String;
public var viewedName:String;
public var userID:String; public var userID:String;
public function ViewCameraEvent(userID:String, stream:String, viewedName:String) public function ViewCameraEvent(userID:String)
{ {
super(VIEW_CAMERA_EVENT,true); super(VIEW_CAMERA_EVENT,true);
this.userID = userID; this.userID = userID;
this.stream = stream;
this.viewedName = viewedName;
} }
} }

View File

@ -194,7 +194,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
</EventHandlers> </EventHandlers>
<EventHandlers type="{ResponseModeratorEvent.RESPONSE_ALL}" > <EventHandlers type="{ResponseModeratorEvent.RESPONSE_ALL}" >
<MethodInvoker generator="{UserService}" method="responseToGuest" arguments="{event}" /> <MethodInvoker generator="{UserService}" method="responseToAllGuest" arguments="{event}" />
</EventHandlers> </EventHandlers>
<EventHandlers type="{BBBEvent.BROADCAST_GUEST_POLICY}" > <EventHandlers type="{BBBEvent.BROADCAST_GUEST_POLICY}" >

View File

@ -112,15 +112,9 @@ package org.bigbluebutton.modules.users.services
case "GuestsWaitingForApprovalEvtMsg": case "GuestsWaitingForApprovalEvtMsg":
handleGuestsWaitingForApprovalEvtMsg(message); handleGuestsWaitingForApprovalEvtMsg(message);
break; break;
case "meetingEnded":
handleLogout(message);
break;
case "MeetingEndingEvtMsg": case "MeetingEndingEvtMsg":
handleMeetingEnding(message); handleMeetingEnding(message);
break; break;
case "meetingHasEnded":
handleMeetingHasEnded(message);
break;
case "meetingMuted": case "meetingMuted":
handleMeetingMuted(message); handleMeetingMuted(message);
break; break;
@ -182,10 +176,10 @@ package org.bigbluebutton.modules.users.services
case "ScreenshareRtmpBroadcastStoppedEvtMsg": case "ScreenshareRtmpBroadcastStoppedEvtMsg":
handleScreenshareRtmpBroadcastStoppedEvtMsg(message); handleScreenshareRtmpBroadcastStoppedEvtMsg(message);
break; break;
case "get_guest_policy_reply": case "GetGuestPolicyRespMsg":
handleGetGuestPolicyReply(message); handleGetGuestPolicyReply(message);
break; break;
case "guest_policy_changed": case "GuestPolicyChangedEvtMsg":
handleGuestPolicyChanged(message); handleGuestPolicyChanged(message);
break; break;
case "guest_access_denied": case "guest_access_denied":
@ -274,6 +268,7 @@ package org.bigbluebutton.modules.users.services
var guestWaiting: GuestWaiting = new GuestWaiting(guest.intId, guest.name, guest.role); var guestWaiting: GuestWaiting = new GuestWaiting(guest.intId, guest.name, guest.role);
LiveMeeting.inst().guestsWaiting.add(guestWaiting); LiveMeeting.inst().guestsWaiting.add(guestWaiting);
} }
private function handleGuestsWaitingForApprovalEvtMsg(msg: Object): void { private function handleGuestsWaitingForApprovalEvtMsg(msg: Object): void {
var body: Object = msg.body as Object; var body: Object = msg.body as Object;
var guests: Array = body.guests as Array; var guests: Array = body.guests as Array;
@ -473,10 +468,6 @@ package org.bigbluebutton.modules.users.services
return; return;
} }
private function handleMeetingHasEnded(msg: Object):void {
LOGGER.debug("*** handleMeetingHasEnded {0} **** \n", [msg.msg]);
}
private function handlePermissionsSettingsChanged(msg:Object):void { private function handlePermissionsSettingsChanged(msg:Object):void {
//LOGGER.debug("handlePermissionsSettingsChanged {0} \n", [msg.msg]); //LOGGER.debug("handlePermissionsSettingsChanged {0} \n", [msg.msg]);
@ -565,23 +556,16 @@ package org.bigbluebutton.modules.users.services
} }
/**
* Called by the server to tell the client that the meeting has ended.
*/
public function handleLogout(msg:Object):void {
var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.END_MEETING_EVENT);
dispatcher.dispatchEvent(endMeetingEvent);
}
/** /**
* This meeting is in the process of ending by the server * This meeting is in the process of ending by the server
*/ */
public function handleMeetingEnding(msg:Object):void { public function handleMeetingEnding(msg:Object):void {
// Avoid trying to reconnect // Avoid trying to reconnect
var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT); var cancelReconnectEvent:BBBEvent = new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT);
dispatcher.dispatchEvent(endMeetingEvent); dispatcher.dispatchEvent(cancelReconnectEvent);
var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.END_MEETING_EVENT);
dispatcher.dispatchEvent(endMeetingEvent);
} }
public function handleAssignPresenterCallback(msg:Object):void { public function handleAssignPresenterCallback(msg:Object):void {
@ -670,7 +654,6 @@ package org.bigbluebutton.modules.users.services
var webUser: User2x = UsersUtil.getUser(userId); var webUser: User2x = UsersUtil.getUser(userId);
if (webUser != null) { if (webUser != null) {
webUser.streamNames.push(streamId);
sendStreamStartedEvent(userId, webUser.name, streamId); sendStreamStartedEvent(userId, webUser.name, streamId);
} }
@ -691,6 +674,8 @@ package org.bigbluebutton.modules.users.services
logData.user.webcamStream = stream; logData.user.webcamStream = stream;
LOGGER.info(JSON.stringify(logData)); LOGGER.info(JSON.stringify(logData));
LiveMeeting.inst().webcams.remove(stream);
sendStreamStoppedEvent(userId, stream); sendStreamStoppedEvent(userId, stream);
} }
@ -699,9 +684,6 @@ package org.bigbluebutton.modules.users.services
dispatcher.dispatchEvent(new StreamStoppedEvent(userId, streamId)); dispatcher.dispatchEvent(new StreamStoppedEvent(userId, streamId));
} }
private function handleBreakoutRoomsList(msg:Object):void{ private function handleBreakoutRoomsList(msg:Object):void{
for each(var room : Object in msg.body.rooms) for each(var room : Object in msg.body.rooms)
@ -774,24 +756,26 @@ package org.bigbluebutton.modules.users.services
} }
} }
public function handleGuestPolicyChanged(msg:Object):void { public function handleGuestPolicyChanged(msg:Object):void {
LOGGER.debug("*** handleGuestPolicyChanged " + msg.msg + " **** \n"); var header: Object = msg.header as Object;
var map:Object = JSON.parse(msg.msg); var body: Object = msg.body as Object;
var policy: String = body.policy as String;
var policy:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY); var policyEvent:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY);
policy.payload['guestPolicy'] = map.guestPolicy; policyEvent.payload['guestPolicy'] = policy;
dispatcher.dispatchEvent(policy); dispatcher.dispatchEvent(policyEvent);
} }
public function handleGetGuestPolicyReply(msg:Object):void { public function handleGetGuestPolicyReply(msg:Object):void {
LOGGER.debug("*** handleGetGuestPolicyReply " + msg.msg + " **** \n"); var header: Object = msg.header as Object;
var map:Object = JSON.parse(msg.msg); var body: Object = msg.body as Object;
var policy: String = body.policy as String;
var policy:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY);
policy.payload['guestPolicy'] = map.guestPolicy; LiveMeeting.inst().guestsWaiting.setGuestPolicy(policy);
dispatcher.dispatchEvent(policy);
var policyEvent:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY);
policyEvent.payload['guestPolicy'] = policyEvent;
dispatcher.dispatchEvent(policyEvent);
} }
public function handleGuestAccessDenied(msg:Object):void { public function handleGuestAccessDenied(msg:Object):void {

View File

@ -37,6 +37,8 @@ package org.bigbluebutton.modules.users.services
import org.bigbluebutton.core.connection.messages.breakoutrooms.RequestBreakoutJoinURLMsg; import org.bigbluebutton.core.connection.messages.breakoutrooms.RequestBreakoutJoinURLMsg;
import org.bigbluebutton.core.connection.messages.breakoutrooms.RequestBreakoutJoinURLMsgBody; import org.bigbluebutton.core.connection.messages.breakoutrooms.RequestBreakoutJoinURLMsgBody;
import org.bigbluebutton.core.managers.ConnectionManager; import org.bigbluebutton.core.managers.ConnectionManager;
import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.core.model.users.GuestWaiting;
public class MessageSender { public class MessageSender {
private static const LOGGER:ILogger = getClassLogger(MessageSender); private static const LOGGER:ILogger = getClassLogger(MessageSender);
@ -577,8 +579,15 @@ package org.bigbluebutton.modules.users.services
public function queryForGuestPolicy():void { public function queryForGuestPolicy():void {
LOGGER.debug("queryForGuestPolicy"); LOGGER.debug("queryForGuestPolicy");
var message:Object = {
header: {name: "GetGuestPolicyReqMsg", meetingId: UsersUtil.getInternalMeetingID(),
userId: UsersUtil.getMyUserID()},
body: {requestedBy: UsersUtil.getMyUserID()}
};
var _nc:ConnectionManager = BBB.initConnectionManager(); var _nc:ConnectionManager = BBB.initConnectionManager();
_nc.sendMessage("participants.getGuestPolicy", _nc.sendMessage2x(
function(result:String):void { // On successful result function(result:String):void { // On successful result
LOGGER.debug(result); LOGGER.debug(result);
}, },
@ -587,14 +596,15 @@ package org.bigbluebutton.modules.users.services
logData.tags = ["apps"]; logData.tags = ["apps"];
logData.message = "Error occured query guest policy."; logData.message = "Error occured query guest policy.";
LOGGER.info(JSON.stringify(logData)); LOGGER.info(JSON.stringify(logData));
} },
JSON.stringify(message)
); );
} }
public function setGuestPolicy(policy:String):void { public function setGuestPolicy(policy:String):void {
LOGGER.debug("setGuestPolicy - new policy:[" + policy + "]"); LOGGER.debug("setGuestPolicy - new policy:[" + policy + "]");
var message:Object = { var message:Object = {
header: {name: "SetGuestPolicyMsg", meetingId: UsersUtil.getInternalMeetingID(), header: {name: "SetGuestPolicyCmdMsg", meetingId: UsersUtil.getInternalMeetingID(),
userId: UsersUtil.getMyUserID()}, userId: UsersUtil.getMyUserID()},
body: {policy: policy, setBy: UsersUtil.getMyUserID()} body: {policy: policy, setBy: UsersUtil.getMyUserID()}
}; };
@ -617,12 +627,17 @@ package org.bigbluebutton.modules.users.services
public function responseToGuest(userId:String, response:Boolean):void { public function responseToGuest(userId:String, response:Boolean):void {
LOGGER.debug("responseToGuest - userId:[" + userId + "] response:[" + response + "]"); LOGGER.debug("responseToGuest - userId:[" + userId + "] response:[" + response + "]");
var message:Object = new Object(); var _guests: Array = new Array();
message["userId"] = userId; _guests.push({guest: userId, approved: response});
message["response"] = response;
var message:Object = {
header: {name: "GuestsWaitingApprovedMsg", meetingId: UsersUtil.getInternalMeetingID(),
userId: UsersUtil.getMyUserID()},
body: {guests: _guests, approvedBy: UsersUtil.getMyUserID()}
};
var _nc:ConnectionManager = BBB.initConnectionManager(); var _nc:ConnectionManager = BBB.initConnectionManager();
_nc.sendMessage("participants.responseToGuest", _nc.sendMessage2x(
function(result:String):void { // On successful result function(result:String):void { // On successful result
LOGGER.debug(result); LOGGER.debug(result);
}, },
@ -632,12 +647,38 @@ package org.bigbluebutton.modules.users.services
logData.message = "Error occured response guest."; logData.message = "Error occured response guest.";
LOGGER.info(JSON.stringify(logData)); LOGGER.info(JSON.stringify(logData));
}, },
message JSON.stringify(message)
); );
} }
public function responseToAllGuests(response:Boolean):void { public function responseToAllGuests(response:Boolean):void {
responseToGuest(null, response); var _guestsWaiting: Array = LiveMeeting.inst().guestsWaiting.getGuests();
var _guests: Array = new Array();
for (var i:int = 0; i < _guests.length; i++) {
var _guest: GuestWaiting = _guestsWaiting[i] as GuestWaiting;
_guests.push({guest: _guest.intId, approved: response});
}
var message:Object = {
header: {name: "GuestsWaitingApprovedMsg", meetingId: UsersUtil.getInternalMeetingID(),
userId: UsersUtil.getMyUserID()},
body: {guests: _guests, approvedBy: UsersUtil.getMyUserID()}
};
var _nc:ConnectionManager = BBB.initConnectionManager();
_nc.sendMessage2x(
function(result:String):void { // On successful result
LOGGER.debug(result);
},
function(status:String):void { // status - On error occurred
var logData:Object = UsersUtil.initLogData();
logData.tags = ["apps"];
logData.message = "Error occured response guest.";
LOGGER.info(JSON.stringify(logData));
},
JSON.stringify(message)
);
} }
} }
} }

View File

@ -142,7 +142,7 @@
} }
private function viewCamera():void { private function viewCamera():void {
dispatchEvent(new ViewCameraEvent(data.userId, data.streamName, data.name)); dispatchEvent(new ViewCameraEvent(data.userId));
} }
private function kickUser():void{ private function kickUser():void{

View File

@ -52,69 +52,77 @@
<mate:Listener type="{UserLeftEvent.LEFT}" method="handleUserLeftEvent" /> <mate:Listener type="{UserLeftEvent.LEFT}" method="handleUserLeftEvent" />
<mate:Listener type="{UserEmojiChangedEvent.USER_EMOJI_CHANGED}" method="handleUserEmojiChangedEvent" /> <mate:Listener type="{UserEmojiChangedEvent.USER_EMOJI_CHANGED}" method="handleUserEmojiChangedEvent" />
<mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" /> <mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" />
<mate:Listener type="{StoppedViewingWebcamEvent.STOPPED_VIEWING_WEBCAM}" method="handleStoppedViewingWebcamEvent" />
<mate:Listener type="{StartedViewingWebcamEvent.STARTED_VIEWING_WEBCAM}" method="handleStartedViewingWebcamEvent" />
<mate:Listener type="{StreamStartedEvent.STREAM_STARTED}" method="handleStreamStartedEvent" />
<mate:Listener type="{StreamStoppedEvent.STREAM_STOPPED}" method="handleStreamStoppedEvent" />
</fx:Declarations> </fx:Declarations>
<fx:Script> <fx:Script>
<![CDATA[ <![CDATA[
import com.asfusion.mate.events.Dispatcher; import com.asfusion.mate.events.Dispatcher;
import flash.net.navigateToURL; import flash.net.navigateToURL;
import mx.binding.utils.BindingUtils; import mx.binding.utils.BindingUtils;
import mx.collections.ArrayCollection; import mx.collections.ArrayCollection;
import mx.controls.Alert; import mx.controls.Alert;
import mx.controls.Menu; import mx.controls.Menu;
import mx.controls.dataGridClasses.DataGridColumn; import mx.controls.dataGridClasses.DataGridColumn;
import mx.controls.listClasses.IListItemRenderer; import mx.controls.listClasses.IListItemRenderer;
import mx.core.FlexGlobals; import mx.core.FlexGlobals;
import mx.core.mx_internal; import mx.core.mx_internal;
import mx.events.CloseEvent; import mx.events.CloseEvent;
import mx.events.CollectionEvent; import mx.events.CollectionEvent;
import mx.events.ListEvent; import mx.events.ListEvent;
import mx.events.MenuEvent; import mx.events.MenuEvent;
import mx.managers.PopUpManager; import mx.managers.PopUpManager;
import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger; import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow; import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.Role; import org.bigbluebutton.common.Role;
import org.bigbluebutton.common.events.LocaleChangeEvent; import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.core.EventConstants; import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.KeyboardUtil; import org.bigbluebutton.core.KeyboardUtil;
import org.bigbluebutton.core.PopUpUtil; import org.bigbluebutton.core.PopUpUtil;
import org.bigbluebutton.core.TimerUtil; import org.bigbluebutton.core.TimerUtil;
import org.bigbluebutton.core.UsersUtil; import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.BreakoutRoomsListUpdatedEvent; import org.bigbluebutton.core.events.BreakoutRoomsListUpdatedEvent;
import org.bigbluebutton.core.events.CoreEvent; import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.events.LockControlEvent; import org.bigbluebutton.core.events.LockControlEvent;
import org.bigbluebutton.core.events.UserEmojiChangedEvent; import org.bigbluebutton.core.events.UserEmojiChangedEvent;
import org.bigbluebutton.core.events.UserStatusChangedEvent; import org.bigbluebutton.core.events.UserStatusChangedEvent;
import org.bigbluebutton.core.events.VoiceConfEvent; import org.bigbluebutton.core.events.VoiceConfEvent;
import org.bigbluebutton.core.model.LiveMeeting; import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.core.model.users.User2x; import org.bigbluebutton.core.model.users.User2x;
import org.bigbluebutton.core.vo.LockSettingsVO; import org.bigbluebutton.core.vo.LockSettingsVO;
import org.bigbluebutton.main.events.BBBEvent; import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.BreakoutRoomEvent; import org.bigbluebutton.main.events.BreakoutRoomEvent;
import org.bigbluebutton.main.events.ShortcutEvent; import org.bigbluebutton.main.events.ShortcutEvent;
import org.bigbluebutton.main.events.SwitchedPresenterEvent; import org.bigbluebutton.main.events.StartedViewingWebcamEvent;
import org.bigbluebutton.main.events.UserJoinedEvent; import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.UserLeftEvent; import org.bigbluebutton.main.events.SwitchedPresenterEvent;
import org.bigbluebutton.main.model.users.BreakoutRoom; import org.bigbluebutton.main.events.UserJoinedEvent;
import org.bigbluebutton.main.model.users.events.ChangeMyRole; import org.bigbluebutton.main.events.UserLeftEvent;
import org.bigbluebutton.main.model.users.events.EmojiStatusEvent; import org.bigbluebutton.main.model.users.BreakoutRoom;
import org.bigbluebutton.main.model.users.events.KickUserEvent; import org.bigbluebutton.main.model.users.events.ChangeMyRole;
import org.bigbluebutton.main.model.users.events.RoleChangeEvent; import org.bigbluebutton.main.model.users.events.EmojiStatusEvent;
import org.bigbluebutton.main.views.MainCanvas; import org.bigbluebutton.main.model.users.events.KickUserEvent;
import org.bigbluebutton.main.views.WellPositionedMenu; import org.bigbluebutton.main.model.users.events.RoleChangeEvent;
import org.bigbluebutton.modules.phone.events.LeaveVoiceConferenceCommand; import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.modules.users.events.MeetingMutedEvent; import org.bigbluebutton.main.model.users.events.StreamStoppedEvent;
import org.bigbluebutton.modules.users.events.UsersRollEvent; import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.users.model.BreakoutRoomsOptions; import org.bigbluebutton.main.views.WellPositionedMenu;
import org.bigbluebutton.modules.users.model.UsersOptions; import org.bigbluebutton.modules.phone.events.LeaveVoiceConferenceCommand;
import org.bigbluebutton.modules.users.views.model.BBBUser2x; import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; import org.bigbluebutton.modules.users.events.UsersRollEvent;
import org.bigbluebutton.util.i18n.ResourceUtil; import org.bigbluebutton.modules.users.model.BreakoutRoomsOptions;
import org.bigbluebutton.modules.users.model.UsersOptions;
import org.bigbluebutton.modules.users.views.model.BBBUser2x;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
private static const LOGGER:ILogger = getClassLogger(UsersWindow); private static const LOGGER:ILogger = getClassLogger(UsersWindow);
@ -196,7 +204,22 @@
handler.handleUserLeftEvent(event.userID); handler.handleUserLeftEvent(event.userID);
} }
private function handleStoppedViewingWebcamEvent(event: StoppedViewingWebcamEvent): void {
handler.handleStoppedViewingWebcamEvent(event);
}
private function handleStartedViewingWebcamEvent(event: StartedViewingWebcamEvent): void {
handler.handleStartedViewingWebcamEvent(event);
}
private function handleStreamStartedEvent(event: StreamStartedEvent): void {
handler.handleStreamStartedEvent(event);
}
private function handleStreamStoppedEvent(event: StreamStoppedEvent): void {
handler.handleStreamStoppedEvent(event);
}
private function handleUserJoinedEvent(event: UserJoinedEvent):void { private function handleUserJoinedEvent(event: UserJoinedEvent):void {
handler.handleUserJoinedEvent(event); handler.handleUserJoinedEvent(event);
} }

View File

@ -8,7 +8,11 @@ package org.bigbluebutton.modules.users.views
import org.bigbluebutton.core.model.LiveMeeting; import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.core.model.users.User2x; import org.bigbluebutton.core.model.users.User2x;
import org.bigbluebutton.core.model.users.VoiceUser2x; import org.bigbluebutton.core.model.users.VoiceUser2x;
import org.bigbluebutton.main.events.StartedViewingWebcamEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.UserJoinedEvent; import org.bigbluebutton.main.events.UserJoinedEvent;
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.main.model.users.events.StreamStoppedEvent;
import org.bigbluebutton.modules.users.views.model.BBBUser2x; import org.bigbluebutton.modules.users.views.model.BBBUser2x;
import org.bigbluebutton.modules.users.views.model.BBBVoiceUser2x; import org.bigbluebutton.modules.users.views.model.BBBVoiceUser2x;
@ -86,15 +90,14 @@ package org.bigbluebutton.modules.users.views
users.refresh(); users.refresh();
} }
private function getWebcamStreamsForUser(userId: String): String { private function getWebcamStreamsForUser(userId: String): Array {
var streamIds: Array = LiveMeeting.inst().webcams.getStreamIdsForUser(userId); var streamIds: Array = LiveMeeting.inst().webcams.getStreamIdsForUser(userId);
var streams:String = ""; return streamIds;
for each(var stream:String in streamIds) { }
streams = streams + stream + "|";
} private function getWebcamStreamsViewingForUser(userId: String): Array {
//Remove last | var streamIds: Array = LiveMeeting.inst().webcams.getStreamIdsIAmViewingForUser(userId);
streams = streams.slice(0, streams.length-1); return streamIds;
return streams;
} }
private function addUser(users: ArrayCollection, user: User2x):void { private function addUser(users: ArrayCollection, user: User2x):void {
@ -107,7 +110,8 @@ package org.bigbluebutton.modules.users.views
buser.locked = user.locked; buser.locked = user.locked;
buser.emojiStatus = user.emoji; buser.emojiStatus = user.emoji;
buser.presenter = user.presenter; buser.presenter = user.presenter;
buser.streamName = getWebcamStreamsForUser(buser.userId); buser.streams = getWebcamStreamsForUser(buser.userId);
buser.viewedStream = getWebcamStreamsViewingForUser(buser.userId);
buser.inVoiceConf = false; buser.inVoiceConf = false;
@ -131,7 +135,6 @@ package org.bigbluebutton.modules.users.views
var user: User2x = UsersUtil.getUser(event.userID); var user: User2x = UsersUtil.getUser(event.userID);
if (user != null) { if (user != null) {
addUser(users, user); addUser(users, user);
trace("!!!!!!!!!!!!!!!********* " + user.name + " " + user.presenter + " ********!!!!!!!!");
users.refresh(); users.refresh();
} }
} }
@ -153,8 +156,6 @@ package org.bigbluebutton.modules.users.views
} }
} }
public function handleUserJoinedVoiceConfEvent(userId: String):void { public function handleUserJoinedVoiceConfEvent(userId: String):void {
var webUser: BBBUser2x = findUser(userId); var webUser: BBBUser2x = findUser(userId);
if (webUser != null) { if (webUser != null) {
@ -258,6 +259,32 @@ package org.bigbluebutton.modules.users.views
users.refresh(); users.refresh();
} }
private function refreshWebcamStreamsInfo(userId: String): void {
var user: BBBUser2x = findUser(userId);
if (user != null) {
user.streams = getWebcamStreamsForUser(user.userId);
user.viewedStream = getWebcamStreamsViewingForUser(user.userId);
}
users.refresh();
}
public function handleStoppedViewingWebcamEvent(event: StoppedViewingWebcamEvent): void {
refreshWebcamStreamsInfo(event.webcamUserID);
}
public function handleStartedViewingWebcamEvent(event: StartedViewingWebcamEvent): void {
refreshWebcamStreamsInfo(event.webcamUserID);
}
public function handleStreamStartedEvent(event: StreamStartedEvent): void {
refreshWebcamStreamsInfo(event.userID);
}
public function handleStreamStoppedEvent(event: StreamStoppedEvent): void {
refreshWebcamStreamsInfo(event.userId);
}
private function findUser(userId: String): BBBUser2x { private function findUser(userId: String): BBBUser2x {
for (var i: int = 0; i < users.length; i++) { for (var i: int = 0; i < users.length; i++) {
var user: BBBUser2x = users[i] as BBBUser2x; var user: BBBUser2x = users[i] as BBBUser2x;

View File

@ -19,15 +19,17 @@
package org.bigbluebutton.modules.users.views.model package org.bigbluebutton.modules.users.views.model
{ {
import com.asfusion.mate.events.Dispatcher; import com.asfusion.mate.events.Dispatcher;
import flash.events.Event; import flash.events.Event;
import org.as3commons.lang.ArrayUtils; import org.as3commons.lang.ArrayUtils;
import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger; import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.Role; import org.bigbluebutton.common.Role;
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
import org.bigbluebutton.main.model.users.Status; import org.bigbluebutton.main.model.users.Status;
import org.bigbluebutton.main.model.users.StatusCollection; import org.bigbluebutton.main.model.users.StatusCollection;
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
public class BBBUser2x { public class BBBUser2x {
private static const LOGGER:ILogger = getClassLogger(BBBUser2x); private static const LOGGER:ILogger = getClassLogger(BBBUser2x);
@ -48,7 +50,7 @@ package org.bigbluebutton.modules.users.views.model
[Bindable] [Bindable]
public function get hasStream():Boolean { public function get hasStream():Boolean {
return streamNames.length > 0; return _streamNames.length > 0;
} }
public function set hasStream(s:Boolean):void { public function set hasStream(s:Boolean):void {
throw new Error("hasStream cannot be set. It is derived directly from streamName"); throw new Error("hasStream cannot be set. It is derived directly from streamName");
@ -56,59 +58,37 @@ package org.bigbluebutton.modules.users.views.model
[Bindable] private var _viewingStream:Array = new Array(); [Bindable] private var _viewingStream:Array = new Array();
[Bindable]
public function get viewingStream():Array {
return _viewingStream;
}
public function set viewingStream(v:Array):void {
throw new Error("Please use the helpers addViewingStream or removeViewingStream to handle viewingStream");
}
public function addViewingStream(streamName:String):Boolean {
if (isViewingStream(streamName)) {
return false;
}
_viewingStream.push(streamName);
return true;
}
public function removeViewingStream(streamName:String):Boolean {
if (!isViewingStream(streamName)) {
return false;
}
_viewingStream = _viewingStream.filter(function(item:*, index:int, array:Array):Boolean { return item != streamName; });
return true;
}
private function isViewingStream(streamName:String):Boolean {
return _viewingStream.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; });
}
public function isViewingAllStreams():Boolean { public function isViewingAllStreams():Boolean {
return _viewingStream.length == streamNames.length; return _viewingStream.length == _streamNames.length;
} }
[Bindable] public var streamNames:Array = new Array(); public function set viewedStream(streamIds: Array): void {
if (streamIds != null) {
_viewingStream = streamIds;
}
}
private var _streamNames:Array = new Array();
[Bindable] [Bindable]
public function get streamName():String { public function get streams():Array {
var streams:String = ""; return _streamNames;
for each(var stream:String in streamNames) {
streams = streams + stream + "|";
}
//Remove last |
streams = streams.slice(0, streams.length-1);
return streams;
} }
private function hasThisStream(streamName:String):Boolean { private function hasThisStream(streamName:String):Boolean {
return streamNames.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; }); for each(var streamId:String in _streamNames) {
if (streamId == streamName) return true;
}
return false;
} }
public function set streamName(streamNames:String):void { public function set streams(streamIds:Array):void {
if(streamNames) { if (streamIds != null) {
var streamNamesList:Array = streamNames.split("|"); _streamNames = streamIds;
for each(var streamName:String in streamNamesList) { // for each(var streamId:String in _streamNames) {
sharedWebcam(streamName); // sharedWebcam(streamId);
} // }
} }
} }
@ -242,19 +222,12 @@ package org.bigbluebutton.modules.users.views.model
public function sharedWebcam(stream: String):void { public function sharedWebcam(stream: String):void {
if(stream && stream != "" && !hasThisStream(stream)) { if(stream && stream != "" && !hasThisStream(stream)) {
streamNames.push(stream);
sendStreamStartedEvent(stream); sendStreamStartedEvent(stream);
} }
buildStatus(); buildStatus();
verifyMedia(); verifyMedia();
} }
public function unsharedWebcam(stream: String):void {
streamNames = streamNames.filter(function(item:*, index:int, array:Array):Boolean { return item != stream });
buildStatus();
verifyMedia();
}
public function presenterStatusChanged(presenter: Boolean):void { public function presenterStatusChanged(presenter: Boolean):void {
this.presenter = presenter; this.presenter = presenter;
buildStatus(); buildStatus();
@ -270,18 +243,6 @@ package org.bigbluebutton.modules.users.views.model
case "presenter": case "presenter":
presenter=(status.value.toString().toUpperCase() == "TRUE") ? true : false; presenter=(status.value.toString().toUpperCase() == "TRUE") ? true : false;
break; break;
case "hasStream":
var streamInfo:Array=String(status.value).split(/,/);
/**
* Cannot use this statement as new Boolean(expression)
* return true if the expression is a non-empty string not
* when the string equals "true". See Boolean class def.
*
* hasStream = new Boolean(String(streamInfo[0]));
*/
var streamNameInfo:Array=String(streamInfo[1]).split(/=/);
streamName=streamNameInfo[1];
break;
// @FIXME : check the coming status from the server // @FIXME : check the coming status from the server
case "emojiStatus": case "emojiStatus":
emojiStatus = status.value.toString(); emojiStatus = status.value.toString();

View File

@ -88,7 +88,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<EventHandlers type="{StreamStartedEvent.STREAM_STARTED}"> <EventHandlers type="{StreamStartedEvent.STREAM_STARTED}">
<ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/> <ObjectBuilder generator="{VideoEventMapDelegate}" cache="global" constructorArguments="{scope.dispatcher}"/>
<MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.user]}" /> <MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID]}" />
</EventHandlers> </EventHandlers>
<EventHandlers type="{StreamStoppedEvent.STREAM_STOPPED}"> <EventHandlers type="{StreamStoppedEvent.STREAM_STOPPED}">
@ -96,7 +96,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
</EventHandlers> </EventHandlers>
<EventHandlers type="{ViewCameraEvent.VIEW_CAMERA_EVENT}"> <EventHandlers type="{ViewCameraEvent.VIEW_CAMERA_EVENT}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.viewedName]}" /> <MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID]}" />
</EventHandlers> </EventHandlers>
<EventHandlers type="{UserJoinedEvent.JOINED}"> <EventHandlers type="{UserJoinedEvent.JOINED}">

View File

@ -114,11 +114,10 @@ package org.bigbluebutton.modules.videoconf.maps
_graphics.addStaticComponent(component); _graphics.addStaticComponent(component);
} }
public function viewCamera(userID:String, stream:String, name:String, mock:Boolean = false):void { public function viewCamera(userID:String):void {
LOGGER.debug("VideoEventMapDelegate:: [{0}] viewCamera. ready = [{1}]", [me, _ready]); LOGGER.debug("VideoEventMapDelegate:: [{0}] viewCamera. ready = [{1}]", [me, _ready]);
if (!_ready) return; if (!_ready) return;
LOGGER.debug("VideoEventMapDelegate:: [{0}] Viewing [{1} stream [{2}]", [me, userID, stream]);
if (! UsersUtil.isMe(userID)) { if (! UsersUtil.isMe(userID)) {
openViewWindowFor(userID); openViewWindowFor(userID);
} }

View File

@ -11,13 +11,15 @@ package org.bigbluebutton.modules.videoconf.views
import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger; import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.BBB; import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.core.UsersUtil; import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.core.model.VideoProfile;
import org.bigbluebutton.main.events.BBBEvent; import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.StartedViewingWebcamEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent; import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.views.VideoWithWarnings; import org.bigbluebutton.main.views.VideoWithWarnings;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
public class UserVideo extends UserGraphic { public class UserVideo extends UserGraphic {
private static const LOGGER:ILogger = getClassLogger(UserVideo); private static const LOGGER:ILogger = getClassLogger(UserVideo);
@ -125,13 +127,29 @@ package org.bigbluebutton.modules.videoconf.views
} }
private function stopViewing():void { private function stopViewing():void {
// Store that I stopped viewing this streamId;
var myUserId: String = UsersUtil.getMyUserID();
LiveMeeting.inst().webcams.stoppedViewingStream(myUserId, _streamName);
var stopEvent:StoppedViewingWebcamEvent = new StoppedViewingWebcamEvent(); var stopEvent:StoppedViewingWebcamEvent = new StoppedViewingWebcamEvent();
stopEvent.webcamUserID = user.intId; stopEvent.webcamUserID = user.intId;
stopEvent.streamName = _streamName; stopEvent.streamName = _streamName;
_dispatcher.dispatchEvent(stopEvent); _dispatcher.dispatchEvent(stopEvent);
user.removeViewingStream(_streamName);
} }
private function startedViewing():void {
// Store that I started viewing this streamId;
var myUserId: String = UsersUtil.getMyUserID();
LiveMeeting.inst().webcams.startedViewingStream(myUserId, _streamName);
var startEvent:StartedViewingWebcamEvent = new StartedViewingWebcamEvent();
startEvent.webcamUserID = user.intId;
startEvent.streamName = _streamName;
_dispatcher.dispatchEvent(startEvent);
}
private function stopPublishing():void { private function stopPublishing():void {
var e:StopBroadcastEvent = new StopBroadcastEvent(); var e:StopBroadcastEvent = new StopBroadcastEvent();
e.stream = _streamName; e.stream = _streamName;
@ -183,7 +201,8 @@ package org.bigbluebutton.modules.videoconf.views
_ns.play(streamName); _ns.play(streamName);
user.addViewingStream(streamName); startedViewing();
invalidateDisplayList(); invalidateDisplayList();
} }