Merge branch 'ritzalam-work-on-meeting-lifcycle-2' into bbb-2x-mconf
This commit is contained in:
commit
d1cc692791
@ -59,21 +59,21 @@ class BigBlueButtonActor(
|
||||
}
|
||||
|
||||
def receive = {
|
||||
// 2x messages
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
// Internal messages
|
||||
case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg)
|
||||
|
||||
case msg: ValidateAuthToken => handleValidateAuthToken(msg)
|
||||
case _ => // do nothing
|
||||
// 2x messages
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
msg.core match {
|
||||
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
|
||||
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
||||
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
|
||||
case m: PubSubPingSysReqMsg => handlePubSubPingSysReqMsg(m)
|
||||
case m: DestroyMeetingSysCmdMsg => handleDestroyMeeting(m)
|
||||
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
||||
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
|
||||
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
|
||||
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
|
||||
case m: PubSubPingSysReqMsg => handlePubSubPingSysReqMsg(m)
|
||||
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 = {
|
||||
val event = MsgBuilder.buildPubSubPongSysRespMsg(msg.body.system, msg.body.timestamp)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
private def handleDestroyMeeting(msg: DestroyMeetingSysCmdMsg): Unit = {
|
||||
private def handleDestroyMeeting(msg: DestroyMeetingInternalMsg): Unit = {
|
||||
|
||||
for {
|
||||
m <- RunningMeetings.findWithId(meetings, msg.body.meetingId)
|
||||
m2 <- RunningMeetings.remove(meetings, msg.body.meetingId)
|
||||
m <- RunningMeetings.findWithId(meetings, msg.meetingId)
|
||||
m2 <- RunningMeetings.remove(meetings, msg.meetingId)
|
||||
} yield {
|
||||
// send the message for MeetingActor to handle too
|
||||
m.actorRef ! msg
|
||||
/** 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)
|
||||
|
||||
// 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)) {
|
||||
// Disconnect all clients
|
||||
|
||||
val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.body.meetingId)
|
||||
val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.meetingId)
|
||||
outGW.send(disconnectEvnt)
|
||||
|
||||
log.info("Destroyed meetingId={}", msg.body.meetingId)
|
||||
val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.body.meetingId)
|
||||
log.info("Destroyed meetingId={}", msg.meetingId)
|
||||
val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.meetingId)
|
||||
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.
|
||||
context.stop(m.actorRef)
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class BigBlueButtonInGW(
|
||||
eventBus.publish(
|
||||
BigBlueButtonEvent(
|
||||
"meeting-manager",
|
||||
new DestroyMeeting(
|
||||
new DestroyMeetingInternalMsg(
|
||||
meetingID
|
||||
)
|
||||
)
|
||||
|
@ -18,13 +18,18 @@ case class IsMeetingActorAliveMessage(meetingId: 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 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 EndMeeting(meetingId: String) extends InMessage
|
||||
case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String, Boolean]) extends InMessage
|
||||
|
@ -1,11 +1,12 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
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
|
||||
with GuestsWaitingApprovedMsgHdlr
|
||||
with SetGuestPolicyMsgHdlr {
|
||||
with SetGuestPolicyMsgHdlr
|
||||
with GetGuestPolicyReqMsgHdlr {
|
||||
|
||||
this: MeetingActor =>
|
||||
|
||||
|
@ -11,8 +11,6 @@ trait PresentationConversionCompletedPubMsgHdlr {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handlePresentationConversionCompletedPubMsg(msg: PresentationConversionCompletedSysPubMsg): Unit = {
|
||||
log.debug("**************** !!!!!PresentationConversionCompletedPubMsg ")
|
||||
|
||||
def broadcastEvent(msg: PresentationConversionCompletedSysPubMsg): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing)
|
||||
|
@ -9,7 +9,7 @@ trait PresentationConversionUpdatePubMsgHdlr {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handlePresentationConversionUpdatePubMsg(msg: PresentationConversionUpdateSysPubMsg): Unit = {
|
||||
log.debug("**************** !!!!!PresentationConversionUpdateSysPubMsg " + msg.body.messageKey)
|
||||
|
||||
def broadcastEvent(msg: PresentationConversionUpdateSysPubMsg): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing)
|
||||
|
@ -9,7 +9,6 @@ trait PresentationPageGeneratedPubMsgHdlr {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handlePresentationPageGeneratedPubMsg(msg: PresentationPageGeneratedSysPubMsg): Unit = {
|
||||
log.debug("**************** !!!!!PresentationPageGeneratedSysPubMsg " + msg.body.messageKey)
|
||||
|
||||
def broadcastEvent(msg: PresentationPageGeneratedSysPubMsg): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
@ -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.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, MeetingActor }
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
|
||||
|
||||
trait ChangeUserEmojiCmdMsgHdlr {
|
||||
this: BaseMeetingActor =>
|
@ -11,8 +11,8 @@ trait GetUsersMeetingReqMsgHdlr extends HandlerHelpers {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleGetUsersMeetingReqMsg(msg: GetUsersMeetingReqMsg): Unit = {
|
||||
sendAllUsersInMeeting(outGW, msg.body.userId, liveMeeting)
|
||||
sendAllVoiceUsersInMeeting(outGW, msg.body.userId, liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId)
|
||||
sendAllUsersInMeeting(msg.body.userId)
|
||||
sendAllVoiceUsersInMeeting(msg.body.userId, liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId)
|
||||
sendAllWebcamStreams(outGW, msg.body.userId, liveMeeting.webcams, liveMeeting.props.meetingProp.intId)
|
||||
}
|
||||
}
|
||||
|
@ -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.core.OutMessageGateway
|
@ -2,56 +2,25 @@ package org.bigbluebutton.core.apps.users
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
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.running.{ BaseMeetingActor, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
trait LogoutAndEndMeetingCmdMsgHdlr {
|
||||
this: UsersApp =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMessageGateway
|
||||
val eventBus: IncomingEventBus
|
||||
|
||||
def handleLogoutAndEndMeetingCmdMsg(msg: LogoutAndEndMeetingCmdMsg) {
|
||||
for {
|
||||
u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||
} yield {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package org.bigbluebutton.core.apps.users
|
||||
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.domain.MeetingInactivityTracker
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, MeetingInactivityTrackerHelper }
|
||||
import org.bigbluebutton.core.domain.{ MeetingInactivityTracker, MeetingState2x }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
|
||||
trait MeetingActivityResponseCmdMsgHdlr {
|
||||
this: UsersApp =>
|
||||
@ -12,14 +13,31 @@ trait MeetingActivityResponseCmdMsgHdlr {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleMeetingActivityResponseCmdMsg(
|
||||
msg: MeetingActivityResponseCmdMsg,
|
||||
tracker: MeetingInactivityTracker
|
||||
): MeetingInactivityTracker = {
|
||||
MeetingInactivityTrackerHelper.processMeetingActivityResponse(
|
||||
props = liveMeeting.props,
|
||||
outGW,
|
||||
msg,
|
||||
tracker
|
||||
)
|
||||
msg: MeetingActivityResponseCmdMsg,
|
||||
state: MeetingState2x
|
||||
): MeetingState2x = {
|
||||
processMeetingActivityResponse(liveMeeting.props, outGW, msg)
|
||||
MeetingInactivityTracker.resetWarningSentAndTimestamp(state)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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.core.OutMessageGateway
|
@ -13,22 +13,7 @@ trait RegisterUserReqMsgHdlr {
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleRegisterUserReqMsg(msg: RegisterUserReqMsg): Unit = {
|
||||
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)
|
||||
}
|
||||
log.debug("RECEIVED RegisterUserReqMsg msg {}", msg)
|
||||
|
||||
def buildUserRegisteredRespMsg(meetingId: String, userId: String, name: String, role: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
@ -38,5 +23,18 @@ trait RegisterUserReqMsgHdlr {
|
||||
val event = UserRegisteredRespMsg(header, body)
|
||||
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)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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.core.OutMessageGateway
|
@ -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.core.OutMessageGateway
|
@ -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.core.OutMessageGateway
|
@ -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.core.OutMessageGateway
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package org.bigbluebutton.core.apps.users
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x }
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait UserLeaveReqMsgHdlr {
|
||||
@ -11,20 +13,26 @@ trait UserLeaveReqMsgHdlr {
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleUserLeaveReqMsg(msg: UserLeaveReqMsg): Unit = {
|
||||
def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||
for {
|
||||
u <- Users2x.remove(liveMeeting.users2x, msg.body.userId)
|
||||
} yield {
|
||||
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u)
|
||||
|
||||
captionApp2x.handleUserLeavingMsg(msg.body.userId)
|
||||
liveMeeting.startCheckingIfWeNeedToEndVoiceConf()
|
||||
stopAutoStartedRecording()
|
||||
|
||||
// send a user left event for the clients to update
|
||||
val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId)
|
||||
outGW.send(userLeftMeetingEvent)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,13 @@ package org.bigbluebutton.core.apps.users
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.bus.IncomingEventBus
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core2.message.handlers.users.ValidateAuthTokenReqMsgHdlr
|
||||
|
||||
class UsersApp(
|
||||
val liveMeeting: LiveMeeting,
|
||||
val outGW: OutMessageGateway
|
||||
val outGW: OutMessageGateway,
|
||||
val eventBus: IncomingEventBus
|
||||
)(implicit val context: ActorContext)
|
||||
|
||||
extends ValidateAuthTokenReqMsgHdlr
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.core.apps.users
|
||||
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
import org.bigbluebutton.core2.message.handlers.users.{ ChangeUserEmojiCmdMsgHdlr, ValidateAuthTokenReqMsgHdlr }
|
||||
|
||||
trait UsersApp2x
|
||||
extends UserLeaveReqMsgHdlr
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,122 @@
|
||||
package org.bigbluebutton.core.domain
|
||||
|
||||
import com.softwaremill.quicklens._
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
||||
case class MeetingInactivityTracker(
|
||||
maxInactivityTimeoutMinutes: Int,
|
||||
warningMinutesBeforeMax: Int,
|
||||
lastActivityTimeInMinutes: Long,
|
||||
warningSent: Boolean,
|
||||
warningSentOnTimeInMinutes: Long
|
||||
val maxInactivityTimeoutMinutes: Int,
|
||||
val warningMinutesBeforeMax: Int,
|
||||
lastActivityTimestamp: Long,
|
||||
warningSent: Boolean,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
@ -6,10 +6,8 @@ import com.softwaremill.quicklens._
|
||||
object RegisteredUsers {
|
||||
def create(userId: String, extId: String, name: String, roles: String,
|
||||
token: String, avatar: String, guest: Boolean, authenticated: Boolean,
|
||||
waitingForAcceptance: Boolean, users: RegisteredUsers): RegisteredUser = {
|
||||
val ru = new RegisteredUser(userId, extId, name, roles, token, avatar, guest, authenticated, waitingForAcceptance)
|
||||
users.save(ru)
|
||||
ru
|
||||
waitingForAcceptance: Boolean): RegisteredUser = {
|
||||
new RegisteredUser(userId, extId, name, roles, token, avatar, guest, authenticated, waitingForAcceptance)
|
||||
}
|
||||
|
||||
def findWithToken(token: String, users: RegisteredUsers): Option[RegisteredUser] = {
|
||||
@ -43,6 +41,10 @@ object RegisteredUsers {
|
||||
} yield users.save(regUser)
|
||||
}
|
||||
|
||||
def add(users: RegisteredUsers, user: RegisteredUser): Vector[RegisteredUser] = {
|
||||
users.save(user)
|
||||
}
|
||||
|
||||
def remove(id: String, users: RegisteredUsers): Option[RegisteredUser] = {
|
||||
users.delete(id)
|
||||
}
|
||||
|
@ -71,6 +71,16 @@ class ReceivedJsonMsgHandlerActor(
|
||||
case DestroyMeetingSysCmdMsg.NAME =>
|
||||
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
|
||||
case GetUsersMeetingReqMsg.NAME =>
|
||||
routeGenericMsg[GetUsersMeetingReqMsg](envelope, jsonNode)
|
||||
|
@ -1,168 +1,192 @@
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.RecordingStatusChanged
|
||||
import org.bigbluebutton.core.{ MessageRecorder, OutMessageGateway }
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder }
|
||||
|
||||
trait HandlerHelpers {
|
||||
|
||||
def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String,
|
||||
valid: Boolean, waitForApproval: Boolean): Unit = {
|
||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(
|
||||
meetingId,
|
||||
userId, authToken, valid, waitForApproval
|
||||
)
|
||||
Sender.send(outGW, event)
|
||||
|
||||
// TODO: Should disconnect user here.
|
||||
}
|
||||
|
||||
def sendValidateAuthTokenRespMsg(outGW: OutMessageGateway, 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(outGW: OutMessageGateway, liveMeeting: LiveMeeting,
|
||||
user: RegisteredUser): Unit = {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
sendValidateAuthTokenRespMsg(outGW, meetingId, user.id, user.authToken, valid = true, waitForApproval = false)
|
||||
|
||||
val guest = GuestWaiting(user.id, user.name, user.role)
|
||||
addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting)
|
||||
notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId)
|
||||
}
|
||||
|
||||
def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = {
|
||||
GuestsWaiting.add(guestsWaitingList, guest)
|
||||
}
|
||||
|
||||
def userValidatedAndNoNeedToWaitForApproval(
|
||||
outGW: OutMessageGateway,
|
||||
liveMeeting: LiveMeeting,
|
||||
user: RegisteredUser
|
||||
): Unit = {
|
||||
|
||||
println("**************** userValidatedAndNoNeedToWaitForApproval")
|
||||
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
sendValidateAuthTokenRespMsg(outGW, 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(outGW, user.id, liveMeeting)
|
||||
sendAllVoiceUsersInMeeting(outGW, user.id, liveMeeting.voiceUsers, meetingId)
|
||||
sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId)
|
||||
userJoinMeeting(outGW, user.authToken, liveMeeting)
|
||||
if (!Users2x.hasPresenter(liveMeeting.users2x)) {
|
||||
automaticallyAssignPresenter(outGW, liveMeeting)
|
||||
}
|
||||
}
|
||||
|
||||
def notifyModeratorsOfGuestWaiting(outGW: OutMessageGateway, 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)
|
||||
}
|
||||
}
|
||||
|
||||
def sendAllUsersInMeeting(outGW: OutMessageGateway, requesterId: String, liveMeeting: LiveMeeting): 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 sendAllWebcamStreams(outGW: OutMessageGateway, requesterId: String, webcams: Webcams, meetingId: String): Unit = {
|
||||
val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams)
|
||||
val webcamStreams = streams.map { u =>
|
||||
val msVO = MediaStreamVO(id = u.stream.id, url = u.stream.url, userId = u.stream.userId,
|
||||
attributes = u.stream.attributes, viewers = u.stream.viewers)
|
||||
|
||||
WebcamStreamVO(streamId = msVO.id, stream = msVO)
|
||||
}
|
||||
|
||||
val event = MsgBuilder.buildGetWebcamStreamsMeetingRespMsg(meetingId, requesterId, webcamStreams)
|
||||
Sender.send(outGW, event)
|
||||
}
|
||||
|
||||
def sendAllVoiceUsersInMeeting(outGW: OutMessageGateway, 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 userJoinMeeting(outGW: OutMessageGateway, authToken: String, liveMeeting: LiveMeeting): Unit = {
|
||||
for {
|
||||
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers)
|
||||
} yield {
|
||||
val userState = UserState(
|
||||
intId = regUser.id,
|
||||
extId = regUser.externId,
|
||||
name = regUser.name,
|
||||
role = regUser.role,
|
||||
guest = regUser.guest,
|
||||
authed = regUser.authed,
|
||||
waitingForAcceptance = regUser.waitingForAcceptance,
|
||||
emoji = "none",
|
||||
presenter = false,
|
||||
locked = false,
|
||||
avatar = regUser.avatarURL
|
||||
)
|
||||
|
||||
Users2x.add(liveMeeting.users2x, userState)
|
||||
|
||||
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, userState)
|
||||
Sender.send(outGW, event)
|
||||
|
||||
MessageRecorder.record(outGW, liveMeeting.props.recordProp.record, event.core)
|
||||
startRecordingIfAutoStart2x(liveMeeting)
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingIfAutoStart2x(liveMeeting: LiveMeeting): Unit = {
|
||||
if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
|
||||
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
|
||||
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
// outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record,
|
||||
// "system", MeetingStatus2x.isRecording(liveMeeting.status)))
|
||||
}
|
||||
}
|
||||
|
||||
def automaticallyAssignPresenter(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
for {
|
||||
moderator <- Users2x.findModerator(liveMeeting.users2x)
|
||||
newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId)
|
||||
} yield {
|
||||
sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.name)
|
||||
}
|
||||
}
|
||||
|
||||
def sendPresenterAssigned(outGW: OutMessageGateway, meetingId: String, intId: String, name: String, assignedBy: String): Unit = {
|
||||
def event = MsgBuilder.buildPresenterAssignedEvtMsg(meetingId, intId, name, assignedBy)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged }
|
||||
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus }
|
||||
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x }
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder }
|
||||
|
||||
trait HandlerHelpers extends SystemConfiguration {
|
||||
|
||||
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 =>
|
||||
val msVO = MediaStreamVO(id = u.stream.id, url = u.stream.url, userId = u.stream.userId,
|
||||
attributes = u.stream.attributes, viewers = u.stream.viewers)
|
||||
|
||||
WebcamStreamVO(streamId = msVO.id, stream = msVO)
|
||||
}
|
||||
|
||||
val event = MsgBuilder.buildGetWebcamStreamsMeetingRespMsg(meetingId, requesterId, webcamStreams)
|
||||
Sender.send(outGW, event)
|
||||
}
|
||||
|
||||
def userJoinMeeting(outGW: OutMessageGateway, authToken: String,
|
||||
liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
|
||||
val nu = for {
|
||||
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers)
|
||||
} yield {
|
||||
UserState(
|
||||
intId = regUser.id,
|
||||
extId = regUser.externId,
|
||||
name = regUser.name,
|
||||
role = regUser.role,
|
||||
guest = regUser.guest,
|
||||
authed = regUser.authed,
|
||||
waitingForAcceptance = regUser.waitingForAcceptance,
|
||||
emoji = "none",
|
||||
presenter = false,
|
||||
locked = false,
|
||||
avatar = regUser.avatarURL
|
||||
)
|
||||
}
|
||||
|
||||
nu match {
|
||||
case Some(newUser) =>
|
||||
Users2x.add(liveMeeting.users2x, newUser)
|
||||
|
||||
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser)
|
||||
Sender.send(outGW, event)
|
||||
startRecordingIfAutoStart2x(liveMeeting)
|
||||
|
||||
if (!state.expiryTracker.userHasJoined) {
|
||||
MeetingExpiryTracker.setUserHasJoined(state)
|
||||
} else {
|
||||
state
|
||||
}
|
||||
|
||||
case None =>
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingIfAutoStart2x(liveMeeting: LiveMeeting): Unit = {
|
||||
if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
|
||||
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
|
||||
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
// outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record,
|
||||
// "system", MeetingStatus2x.isRecording(liveMeeting.status)))
|
||||
}
|
||||
}
|
||||
|
||||
def automaticallyAssignPresenter(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
for {
|
||||
moderator <- Users2x.findModerator(liveMeeting.users2x)
|
||||
newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId)
|
||||
} yield {
|
||||
sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.name)
|
||||
}
|
||||
}
|
||||
|
||||
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 endMeeting(outGW: OutMessageGateway, liveMeeting: LiveMeeting, reason: String): 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, reason)
|
||||
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)
|
||||
}
|
||||
|
||||
def destroyMeeting(eventBus: IncomingEventBus, meetingId: String): Unit = {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingManagerChannel, new DestroyMeetingInternalMsg(meetingId)))
|
||||
}
|
||||
|
||||
def notifyParentThatBreakoutEnded(eventBus: IncomingEventBus, liveMeeting: LiveMeeting): Unit = {
|
||||
if (liveMeeting.props.meetingProp.isBreakout) {
|
||||
eventBus.publish(BigBlueButtonEvent(
|
||||
liveMeeting.props.breakoutProps.parentId,
|
||||
new BreakoutRoomEndedInternalMsg(liveMeeting.props.meetingProp.intId)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
def ejectAllUsersFromVoiceConf(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
|
||||
val event = MsgBuilder.buildEjectAllFromVoiceConfMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
def sendEndMeetingDueToExpiry(reason: String, eventBus: IncomingEventBus, outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
|
||||
endMeeting(outGW, liveMeeting, reason)
|
||||
notifyParentThatBreakoutEnded(eventBus, liveMeeting)
|
||||
ejectAllUsersFromVoiceConf(outGW, liveMeeting)
|
||||
destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId)
|
||||
}
|
||||
|
||||
def sendEndMeetingDueToExpiry2(reason: String, eventBus: IncomingEventBus, outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
|
||||
val endMeetingEvt = buildMeetingEndingEvtMsg(reason, meetingId)
|
||||
outGW.send(endMeetingEvt)
|
||||
|
||||
val endedEvt = buildMeetingEndedEvtMsg(meetingId)
|
||||
outGW.send(endedEvt)
|
||||
|
||||
if (liveMeeting.props.meetingProp.isBreakout) {
|
||||
eventBus.publish(BigBlueButtonEvent(
|
||||
liveMeeting.props.breakoutProps.parentId,
|
||||
new BreakoutRoomEndedInternalMsg(meetingId)
|
||||
))
|
||||
}
|
||||
|
||||
val event = MsgBuilder.buildEjectAllFromVoiceConfMsg(meetingId, liveMeeting.props.voiceProp.voiceConf)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.domain.Meeting3x
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
@ -29,32 +26,6 @@ class LiveMeeting(
|
||||
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) {
|
||||
MeetingStatus2x.lockLayout(status, lock)
|
||||
}
|
||||
|
@ -1,434 +1,394 @@
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
import org.bigbluebutton.core.apps.users.UsersApp
|
||||
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
//import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.core._
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.apps.caption.CaptionApp2x
|
||||
import org.bigbluebutton.core.apps.chat.ChatApp2x
|
||||
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
|
||||
import org.bigbluebutton.core.apps.presentation.PresentationApp2x
|
||||
import org.bigbluebutton.core.apps.meeting._
|
||||
import org.bigbluebutton.core.apps.users.UsersApp2x
|
||||
import org.bigbluebutton.core.apps.sharednotes.SharedNotesApp2x
|
||||
import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.handlers._
|
||||
import org.bigbluebutton.core2.message.handlers.users._
|
||||
import org.bigbluebutton.core2.message.handlers.meeting._
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.breakout._
|
||||
import org.bigbluebutton.core.apps.polls._
|
||||
import org.bigbluebutton.core.apps.voice._
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.core2.testdata.FakeTestData
|
||||
import org.bigbluebutton.core.apps.layout.LayoutApp2x
|
||||
import org.bigbluebutton.core.apps.meeting.SyncGetMeetingInfoRespMsgHdlr
|
||||
|
||||
object MeetingActor {
|
||||
def props(
|
||||
props: DefaultProps,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway,
|
||||
liveMeeting: LiveMeeting
|
||||
): Props =
|
||||
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
|
||||
}
|
||||
|
||||
class MeetingActor(
|
||||
val props: DefaultProps,
|
||||
val eventBus: IncomingEventBus,
|
||||
val outGW: OutMessageGateway,
|
||||
val liveMeeting: LiveMeeting
|
||||
)
|
||||
extends BaseMeetingActor
|
||||
with GuestsApp
|
||||
with LayoutApp2x
|
||||
with VoiceApp2x
|
||||
with PollApp2x
|
||||
with BreakoutApp2x
|
||||
with UsersApp2x
|
||||
with WhiteboardApp2x
|
||||
|
||||
with PermisssionCheck
|
||||
with UserBroadcastCamStartMsgHdlr
|
||||
with UserJoinMeetingReqMsgHdlr
|
||||
with UserBroadcastCamStopMsgHdlr
|
||||
with UserConnectedToGlobalAudioMsgHdlr
|
||||
with UserDisconnectedFromGlobalAudioMsgHdlr
|
||||
with MuteAllExceptPresentersCmdMsgHdlr
|
||||
with MuteMeetingCmdMsgHdlr
|
||||
with IsMeetingMutedReqMsgHdlr
|
||||
with MuteUserCmdMsgHdlr
|
||||
with EjectUserFromVoiceCmdMsgHdlr
|
||||
with EndMeetingSysCmdMsgHdlr
|
||||
with DestroyMeetingSysCmdMsgHdlr
|
||||
with SendTimeRemainingUpdateHdlr
|
||||
with SyncGetMeetingInfoRespMsgHdlr {
|
||||
|
||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||
case e: Exception => {
|
||||
val sw: StringWriter = new StringWriter()
|
||||
sw.write("An exception has been thrown on MeetingActor, exception message [" + e.getMessage() + "] (full stacktrace below)\n")
|
||||
e.printStackTrace(new PrintWriter(sw))
|
||||
log.error(sw.toString())
|
||||
Resume
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the internal message injector into another actor so this
|
||||
* actor is easy to test.
|
||||
*/
|
||||
var actorMonitor = context.actorOf(
|
||||
MeetingActorAudit.props(props, eventBus, outGW),
|
||||
"actorMonitor-" + props.meetingProp.intId
|
||||
)
|
||||
|
||||
val presentationApp2x = new PresentationApp2x(liveMeeting, outGW)
|
||||
val screenshareApp2x = new ScreenshareApp2x(liveMeeting, outGW)
|
||||
val captionApp2x = new CaptionApp2x(liveMeeting, outGW)
|
||||
val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW)
|
||||
val chatApp2x = new ChatApp2x(liveMeeting, outGW)
|
||||
val usersApp = new UsersApp(liveMeeting, outGW)
|
||||
|
||||
var inactivityTracker = new MeetingInactivityTracker(
|
||||
liveMeeting.props.durationProps.maxInactivityTimeoutMinutes,
|
||||
liveMeeting.props.durationProps.warnMinutesBeforeMax,
|
||||
TimeUtil.millisToMinutes(System.currentTimeMillis()), false, 0L
|
||||
)
|
||||
|
||||
var expiryTracker = new MeetingExpiryTracker(
|
||||
TimeUtil.millisToMinutes(System.currentTimeMillis()),
|
||||
false, 0L
|
||||
)
|
||||
|
||||
/*******************************************************************/
|
||||
//object FakeTestData extends FakeTestData
|
||||
//FakeTestData.createFakeUsers(liveMeeting)
|
||||
/*******************************************************************/
|
||||
|
||||
def receive = {
|
||||
//=============================
|
||||
// 2x messages
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
|
||||
// Handling RegisterUserReqMsg as it is forwarded from BBBActor and
|
||||
// its type is not BbbCommonEnvCoreMsg
|
||||
case m: RegisterUserReqMsg => usersApp.handleRegisterUserReqMsg(m)
|
||||
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
|
||||
|
||||
// Meeting
|
||||
case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m)
|
||||
|
||||
//======================================
|
||||
|
||||
//=======================================
|
||||
// old messages
|
||||
case msg: MonitorNumberOfUsers => handleMonitorNumberOfUsers(msg)
|
||||
|
||||
case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg)
|
||||
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
|
||||
case msg: SendTimeRemainingUpdate => handleSendTimeRemainingUpdate(msg)
|
||||
|
||||
// Screenshare
|
||||
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
|
||||
// Guest
|
||||
case msg: GetGuestPolicy => handleGetGuestPolicy(msg)
|
||||
case msg: SetGuestPolicy => handleSetGuestPolicy(msg)
|
||||
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
// TODO: Update meeting activity status here
|
||||
// updateActivityStatus(msg)
|
||||
|
||||
msg.core match {
|
||||
// Users
|
||||
case m: ValidateAuthTokenReqMsg => usersApp.handleValidateAuthTokenReqMsg(m)
|
||||
case m: UserJoinMeetingReqMsg => handleUserJoinMeetingReqMsg(m)
|
||||
case m: UserLeaveReqMsg => handleUserLeaveReqMsg(m)
|
||||
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
|
||||
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
|
||||
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
|
||||
case m: MeetingActivityResponseCmdMsg => inactivityTracker = usersApp.handleMeetingActivityResponseCmdMsg(m, inactivityTracker)
|
||||
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m)
|
||||
case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m)
|
||||
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
|
||||
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
|
||||
case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m)
|
||||
case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m)
|
||||
|
||||
// Whiteboard
|
||||
case m: SendCursorPositionPubMsg => handleSendCursorPositionPubMsg(m)
|
||||
case m: ClearWhiteboardPubMsg => handleClearWhiteboardPubMsg(m)
|
||||
case m: UndoWhiteboardPubMsg => handleUndoWhiteboardPubMsg(m)
|
||||
case m: ModifyWhiteboardAccessPubMsg => handleModifyWhiteboardAccessPubMsg(m)
|
||||
case m: GetWhiteboardAccessReqMsg => handleGetWhiteboardAccessReqMsg(m)
|
||||
case m: SendWhiteboardAnnotationPubMsg => handleSendWhiteboardAnnotationPubMsg(m)
|
||||
case m: GetWhiteboardAnnotationsReqMsg => handleGetWhiteboardAnnotationsReqMsg(m)
|
||||
|
||||
// Poll
|
||||
case m: StartPollReqMsg => handleStartPollReqMsg(m)
|
||||
case m: StartCustomPollReqMsg => handleStartCustomPollReqMsg(m)
|
||||
case m: StopPollReqMsg => handleStopPollReqMsg(m)
|
||||
case m: ShowPollResultReqMsg => handleShowPollResultReqMsg(m)
|
||||
case m: HidePollResultReqMsg => handleHidePollResultReqMsg(m)
|
||||
case m: GetCurrentPollReqMsg => handleGetCurrentPollReqMsg(m)
|
||||
case m: RespondToPollReqMsg => handleRespondToPollReqMsg(m)
|
||||
|
||||
// Breakout
|
||||
case m: BreakoutRoomsListMsg => handleBreakoutRoomsListMsg(m)
|
||||
case m: CreateBreakoutRoomsCmdMsg => handleCreateBreakoutRoomsCmdMsg(m)
|
||||
case m: EndAllBreakoutRoomsMsg => handleEndAllBreakoutRoomsMsg(m)
|
||||
case m: RequestBreakoutJoinURLReqMsg => handleRequestBreakoutJoinURLReqMsg(m)
|
||||
case m: BreakoutRoomCreatedMsg => handleBreakoutRoomCreatedMsg(m)
|
||||
case m: BreakoutRoomEndedMsg => handleBreakoutRoomEndedMsg(m)
|
||||
case m: BreakoutRoomUsersUpdateMsg => handleBreakoutRoomUsersUpdateMsg(m)
|
||||
case m: SendBreakoutUsersUpdateMsg => handleSendBreakoutUsersUpdateMsg(m)
|
||||
case m: TransferUserToMeetingRequestMsg => handleTransferUserToMeetingRequestMsg(m)
|
||||
|
||||
// Voice
|
||||
case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m)
|
||||
case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m)
|
||||
case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m)
|
||||
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
|
||||
case m: MuteUserCmdMsg => handleMuteUserCmdMsg(m)
|
||||
case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m)
|
||||
case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m)
|
||||
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
|
||||
case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m)
|
||||
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m)
|
||||
case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m)
|
||||
|
||||
// Layout
|
||||
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)
|
||||
case m: LockLayoutMsg => handleLockLayoutMsg(m)
|
||||
case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m)
|
||||
|
||||
// Presentation
|
||||
case m: SetCurrentPresentationPubMsg => presentationApp2x.handleSetCurrentPresentationPubMsg(m)
|
||||
case m: GetPresentationInfoReqMsg => presentationApp2x.handleGetPresentationInfoReqMsg(m)
|
||||
case m: SetCurrentPagePubMsg => presentationApp2x.handleSetCurrentPagePubMsg(m)
|
||||
case m: ResizeAndMovePagePubMsg => presentationApp2x.handleResizeAndMovePagePubMsg(m)
|
||||
case m: RemovePresentationPubMsg => presentationApp2x.handleRemovePresentationPubMsg(m)
|
||||
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handlePreuploadedPresentationsPubMsg(m)
|
||||
case m: PresentationConversionUpdateSysPubMsg => presentationApp2x.handlePresentationConversionUpdatePubMsg(m)
|
||||
case m: PresentationPageCountErrorSysPubMsg => presentationApp2x.handlePresentationPageCountErrorPubMsg(m)
|
||||
case m: PresentationPageGeneratedSysPubMsg => presentationApp2x.handlePresentationPageGeneratedPubMsg(m)
|
||||
case m: PresentationConversionCompletedSysPubMsg => presentationApp2x.handlePresentationConversionCompletedPubMsg(m)
|
||||
case m: AssignPresenterReqMsg => handlePresenterChange(m)
|
||||
|
||||
// Caption
|
||||
case m: EditCaptionHistoryPubMsg => captionApp2x.handleEditCaptionHistoryPubMsg(m)
|
||||
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handleUpdateCaptionOwnerPubMsg(m)
|
||||
case m: SendCaptionHistoryReqMsg => captionApp2x.handleSendCaptionHistoryReqMsg(m)
|
||||
|
||||
// SharedNotes
|
||||
case m: GetSharedNotesPubMsg => sharedNotesApp2x.handleGetSharedNotesPubMsg(m)
|
||||
case m: SyncSharedNotePubMsg => sharedNotesApp2x.handleSyncSharedNotePubMsg(m)
|
||||
case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handleUpdateSharedNoteReqMsg(m)
|
||||
case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handleCreateSharedNoteReqMsg(m)
|
||||
case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handleDestroySharedNoteReqMsg(m)
|
||||
|
||||
// Guests
|
||||
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
|
||||
case m: SetGuestPolicyMsg => handleSetGuestPolicyMsg(m)
|
||||
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
|
||||
|
||||
// Chat
|
||||
case m: GetChatHistoryReqMsg => chatApp2x.handleGetChatHistoryReqMsg(m)
|
||||
case m: SendPublicMessagePubMsg => chatApp2x.handleSendPublicMessagePubMsg(m)
|
||||
case m: SendPrivateMessagePubMsg => chatApp2x.handleSendPrivateMessagePubMsg(m)
|
||||
case m: ClearPublicChatHistoryPubMsg => chatApp2x.handleClearPublicChatHistoryPubMsg(m)
|
||||
|
||||
// Screenshare
|
||||
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStartedVoiceConfEvtMsg(m)
|
||||
case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(m)
|
||||
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(m)
|
||||
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg(m)
|
||||
case m: GetScreenshareStatusReqMsg => screenshareApp2x.handleGetScreenshareStatusReqMsg(m)
|
||||
|
||||
case _ => log.warning("***** Cannot handle " + msg.envelope.name)
|
||||
}
|
||||
}
|
||||
|
||||
def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = {
|
||||
// sync all meetings
|
||||
handleSyncGetMeetingInfoRespMsg(liveMeeting.props)
|
||||
|
||||
// sync all users
|
||||
usersApp.handleSyncGetUsersMeetingRespMsg()
|
||||
|
||||
// sync all presentations
|
||||
presentationApp2x.handleSyncGetPresentationInfoRespMsg()
|
||||
|
||||
// TODO send all chat
|
||||
// TODO send all lock settings
|
||||
// TODO send all screen sharing info
|
||||
}
|
||||
|
||||
def handlePresenterChange(msg: AssignPresenterReqMsg): Unit = {
|
||||
// Stop poll if one is running as presenter left
|
||||
handleStopPollReqMsg(msg.header.userId)
|
||||
|
||||
// switch user presenter status for old and new presenter
|
||||
usersApp.handleAssignPresenterReqMsg(msg)
|
||||
|
||||
// TODO stop current screen sharing session (initiated by the old presenter)
|
||||
|
||||
}
|
||||
|
||||
def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
|
||||
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting="
|
||||
+ ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel) + " URL:" +
|
||||
ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel))
|
||||
|
||||
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),
|
||||
// DeskshareModel.getDesktopShareVideoWidth(liveMeeting.deskshareModel),
|
||||
// DeskshareModel.getDesktopShareVideoHeight(liveMeeting.deskshareModel), true))
|
||||
}
|
||||
}
|
||||
|
||||
def handleGetGuestPolicy(msg: GetGuestPolicy) {
|
||||
// outGW.send(new GetGuestPolicyReply(msg.meetingID, props.recordProp.record,
|
||||
// msg.requesterID, MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString()))
|
||||
}
|
||||
|
||||
def handleSetGuestPolicy(msg: SetGuestPolicy) {
|
||||
// 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) => {
|
||||
val allowed = msg.userID equals (curPres.intId)
|
||||
// outGW.send(AllowUserToShareDesktopOut(msg.meetingID, msg.userID, allowed))
|
||||
}
|
||||
case None => // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsers) {
|
||||
inactivityTracker = MeetingInactivityTrackerHelper.processMeetingInactivityAudit(
|
||||
props = liveMeeting.props,
|
||||
outGW,
|
||||
eventBus,
|
||||
inactivityTracker
|
||||
)
|
||||
|
||||
expiryTracker = MeetingExpiryTracker.processMeetingExpiryAudit(liveMeeting.props, expiryTracker, eventBus)
|
||||
|
||||
monitorNumberOfWebUsers()
|
||||
monitorNumberOfUsers()
|
||||
}
|
||||
|
||||
def monitorNumberOfWebUsers() {
|
||||
|
||||
def buildEjectAllFromVoiceConfMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(EjectAllFromVoiceConfMsg.NAME, routing)
|
||||
val body = EjectAllFromVoiceConfMsgBody(voiceConf)
|
||||
val header = BbbCoreHeaderWithMeetingId(EjectAllFromVoiceConfMsg.NAME, meetingId)
|
||||
val event = EjectAllFromVoiceConfMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
if (Users2x.numUsers(liveMeeting.users2x) == 0 &&
|
||||
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 monitorNumberOfUsers() {
|
||||
val hasUsers = Users2x.numUsers(liveMeeting.users2x) != 0
|
||||
// TODO: We could use a better control over this message to send it just when it really matters :)
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, UpdateMeetingExpireMonitor(props.meetingProp.intId, hasUsers)))
|
||||
}
|
||||
|
||||
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
|
||||
|
||||
}
|
||||
|
||||
def startRecordingIfAutoStart() {
|
||||
if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
|
||||
props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
|
||||
log.info("Auto start recording. meetingId={}", props.meetingProp.intId)
|
||||
MeetingStatus2x.recordingStarted(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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
import org.bigbluebutton.core.apps.users._
|
||||
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
//import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.SupervisorStrategy.Resume
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.core._
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.apps.caption.CaptionApp2x
|
||||
import org.bigbluebutton.core.apps.chat.ChatApp2x
|
||||
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
|
||||
import org.bigbluebutton.core.apps.presentation.PresentationApp2x
|
||||
import org.bigbluebutton.core.apps.meeting._
|
||||
import org.bigbluebutton.core.apps.users.UsersApp2x
|
||||
import org.bigbluebutton.core.apps.sharednotes.SharedNotesApp2x
|
||||
import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.handlers._
|
||||
import org.bigbluebutton.core2.message.handlers.users._
|
||||
import org.bigbluebutton.core2.message.handlers.meeting._
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.breakout._
|
||||
import org.bigbluebutton.core.apps.polls._
|
||||
import org.bigbluebutton.core.apps.voice._
|
||||
import com.softwaremill.quicklens._
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.core2.testdata.FakeTestData
|
||||
import org.bigbluebutton.core.apps.layout.LayoutApp2x
|
||||
import org.bigbluebutton.core.apps.meeting.SyncGetMeetingInfoRespMsgHdlr
|
||||
|
||||
object MeetingActor {
|
||||
def props(
|
||||
props: DefaultProps,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway,
|
||||
liveMeeting: LiveMeeting
|
||||
): Props =
|
||||
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
|
||||
}
|
||||
|
||||
class MeetingActor(
|
||||
val props: DefaultProps,
|
||||
val eventBus: IncomingEventBus,
|
||||
val outGW: OutMessageGateway,
|
||||
val liveMeeting: LiveMeeting
|
||||
)
|
||||
extends BaseMeetingActor
|
||||
with GuestsApp
|
||||
with LayoutApp2x
|
||||
with VoiceApp2x
|
||||
with PollApp2x
|
||||
with BreakoutApp2x
|
||||
with UsersApp2x
|
||||
with WhiteboardApp2x
|
||||
|
||||
with PermisssionCheck
|
||||
with UserBroadcastCamStartMsgHdlr
|
||||
with UserJoinMeetingReqMsgHdlr
|
||||
with UserBroadcastCamStopMsgHdlr
|
||||
with UserConnectedToGlobalAudioMsgHdlr
|
||||
with UserDisconnectedFromGlobalAudioMsgHdlr
|
||||
with MuteAllExceptPresentersCmdMsgHdlr
|
||||
with MuteMeetingCmdMsgHdlr
|
||||
with IsMeetingMutedReqMsgHdlr
|
||||
with MuteUserCmdMsgHdlr
|
||||
with EjectUserFromVoiceCmdMsgHdlr
|
||||
with EndMeetingSysCmdMsgHdlr
|
||||
with DestroyMeetingSysCmdMsgHdlr
|
||||
with SendTimeRemainingUpdateHdlr
|
||||
with SyncGetMeetingInfoRespMsgHdlr {
|
||||
|
||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||
case e: Exception => {
|
||||
val sw: StringWriter = new StringWriter()
|
||||
sw.write("An exception has been thrown on MeetingActor, exception message [" + e.getMessage() + "] (full stacktrace below)\n")
|
||||
e.printStackTrace(new PrintWriter(sw))
|
||||
log.error(sw.toString())
|
||||
Resume
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the internal message injector into another actor so this
|
||||
* actor is easy to test.
|
||||
*/
|
||||
var actorMonitor = context.actorOf(
|
||||
MeetingActorAudit.props(props, eventBus, outGW),
|
||||
"actorMonitor-" + props.meetingProp.intId
|
||||
)
|
||||
|
||||
val presentationApp2x = new PresentationApp2x(liveMeeting, outGW)
|
||||
val screenshareApp2x = new ScreenshareApp2x(liveMeeting, outGW)
|
||||
val captionApp2x = new CaptionApp2x(liveMeeting, outGW)
|
||||
val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW)
|
||||
val chatApp2x = new ChatApp2x(liveMeeting, outGW)
|
||||
val usersApp = new UsersApp(liveMeeting, outGW, eventBus)
|
||||
|
||||
val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW, eventBus)
|
||||
|
||||
val inactivityTracker = new MeetingInactivityTracker(
|
||||
props.durationProps.maxInactivityTimeoutMinutes,
|
||||
props.durationProps.warnMinutesBeforeMax,
|
||||
lastActivityTimestamp = TimeUtil.timeNowInSeconds(),
|
||||
warningSent = false,
|
||||
warningSentOnTimestamp = 0L
|
||||
)
|
||||
|
||||
val expiryTracker = new MeetingExpiryTracker(
|
||||
startedOn = TimeUtil.timeNowInSeconds(),
|
||||
userHasJoined = false,
|
||||
lastUserLeftOn = None,
|
||||
durationInMinutes = props.durationProps.duration,
|
||||
meetingExpireIfNoUserJoinedInMinutes = props.durationProps.meetingExpireIfNoUserJoinedInMinutes,
|
||||
meetingExpireWhenLastUserLeftInMinutes = props.durationProps.meetingExpireWhenLastUserLeftInMinutes
|
||||
)
|
||||
|
||||
var state = new MeetingState2x(inactivityTracker, expiryTracker)
|
||||
|
||||
/*******************************************************************/
|
||||
//object FakeTestData extends FakeTestData
|
||||
//FakeTestData.createFakeUsers(liveMeeting)
|
||||
/*******************************************************************/
|
||||
|
||||
def receive = {
|
||||
//=============================
|
||||
// 2x messages
|
||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||
|
||||
// Handling RegisterUserReqMsg as it is forwarded from BBBActor and
|
||||
// its type is not BbbCommonEnvCoreMsg
|
||||
case m: RegisterUserReqMsg => usersApp.handleRegisterUserReqMsg(m)
|
||||
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
|
||||
|
||||
// Meeting
|
||||
case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m)
|
||||
|
||||
//======================================
|
||||
|
||||
//=======================================
|
||||
// old messages
|
||||
case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg)
|
||||
|
||||
case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg)
|
||||
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
|
||||
case msg: SendTimeRemainingUpdate => state = handleSendTimeRemainingUpdate(msg, state)
|
||||
|
||||
// Screenshare
|
||||
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
state = MeetingInactivityTracker.updateLastActivityTimestamp(state, TimeUtil.timeNowInSeconds())
|
||||
|
||||
msg.core match {
|
||||
// Users
|
||||
case m: ValidateAuthTokenReqMsg =>
|
||||
state = usersApp.handleValidateAuthTokenReqMsg(m, state)
|
||||
case m: UserJoinMeetingReqMsg =>
|
||||
state = handleUserJoinMeetingReqMsg(m, state)
|
||||
case m: UserLeaveReqMsg =>
|
||||
state = handleUserLeaveReqMsg(m, state)
|
||||
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
|
||||
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
|
||||
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
|
||||
case m: MeetingActivityResponseCmdMsg =>
|
||||
state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
|
||||
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m)
|
||||
case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m)
|
||||
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
|
||||
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
|
||||
case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m)
|
||||
case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m)
|
||||
|
||||
// Whiteboard
|
||||
case m: SendCursorPositionPubMsg => handleSendCursorPositionPubMsg(m)
|
||||
case m: ClearWhiteboardPubMsg => handleClearWhiteboardPubMsg(m)
|
||||
case m: UndoWhiteboardPubMsg => handleUndoWhiteboardPubMsg(m)
|
||||
case m: ModifyWhiteboardAccessPubMsg => handleModifyWhiteboardAccessPubMsg(m)
|
||||
case m: GetWhiteboardAccessReqMsg => handleGetWhiteboardAccessReqMsg(m)
|
||||
case m: SendWhiteboardAnnotationPubMsg => handleSendWhiteboardAnnotationPubMsg(m)
|
||||
case m: GetWhiteboardAnnotationsReqMsg => handleGetWhiteboardAnnotationsReqMsg(m)
|
||||
|
||||
// Poll
|
||||
case m: StartPollReqMsg => handleStartPollReqMsg(m)
|
||||
case m: StartCustomPollReqMsg => handleStartCustomPollReqMsg(m)
|
||||
case m: StopPollReqMsg => handleStopPollReqMsg(m)
|
||||
case m: ShowPollResultReqMsg => handleShowPollResultReqMsg(m)
|
||||
case m: HidePollResultReqMsg => handleHidePollResultReqMsg(m)
|
||||
case m: GetCurrentPollReqMsg => handleGetCurrentPollReqMsg(m)
|
||||
case m: RespondToPollReqMsg => handleRespondToPollReqMsg(m)
|
||||
|
||||
// Breakout
|
||||
case m: BreakoutRoomsListMsg => handleBreakoutRoomsListMsg(m)
|
||||
case m: CreateBreakoutRoomsCmdMsg => handleCreateBreakoutRoomsCmdMsg(m)
|
||||
case m: EndAllBreakoutRoomsMsg => handleEndAllBreakoutRoomsMsg(m)
|
||||
case m: RequestBreakoutJoinURLReqMsg => handleRequestBreakoutJoinURLReqMsg(m)
|
||||
case m: BreakoutRoomCreatedMsg => handleBreakoutRoomCreatedMsg(m)
|
||||
case m: BreakoutRoomEndedMsg => handleBreakoutRoomEndedMsg(m)
|
||||
case m: BreakoutRoomUsersUpdateMsg => handleBreakoutRoomUsersUpdateMsg(m)
|
||||
case m: SendBreakoutUsersUpdateMsg => handleSendBreakoutUsersUpdateMsg(m)
|
||||
case m: TransferUserToMeetingRequestMsg => handleTransferUserToMeetingRequestMsg(m)
|
||||
|
||||
// Voice
|
||||
case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m)
|
||||
case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m)
|
||||
case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m)
|
||||
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
|
||||
case m: MuteUserCmdMsg => handleMuteUserCmdMsg(m)
|
||||
case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m)
|
||||
case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m)
|
||||
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
|
||||
case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m)
|
||||
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m)
|
||||
case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m)
|
||||
|
||||
// Layout
|
||||
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)
|
||||
case m: LockLayoutMsg => handleLockLayoutMsg(m)
|
||||
case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m)
|
||||
|
||||
// Presentation
|
||||
case m: SetCurrentPresentationPubMsg => presentationApp2x.handleSetCurrentPresentationPubMsg(m)
|
||||
case m: GetPresentationInfoReqMsg => presentationApp2x.handleGetPresentationInfoReqMsg(m)
|
||||
case m: SetCurrentPagePubMsg => presentationApp2x.handleSetCurrentPagePubMsg(m)
|
||||
case m: ResizeAndMovePagePubMsg => presentationApp2x.handleResizeAndMovePagePubMsg(m)
|
||||
case m: RemovePresentationPubMsg => presentationApp2x.handleRemovePresentationPubMsg(m)
|
||||
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handlePreuploadedPresentationsPubMsg(m)
|
||||
case m: PresentationConversionUpdateSysPubMsg => presentationApp2x.handlePresentationConversionUpdatePubMsg(m)
|
||||
case m: PresentationPageCountErrorSysPubMsg => presentationApp2x.handlePresentationPageCountErrorPubMsg(m)
|
||||
case m: PresentationPageGeneratedSysPubMsg => presentationApp2x.handlePresentationPageGeneratedPubMsg(m)
|
||||
case m: PresentationConversionCompletedSysPubMsg => presentationApp2x.handlePresentationConversionCompletedPubMsg(m)
|
||||
case m: AssignPresenterReqMsg => handlePresenterChange(m)
|
||||
|
||||
// Caption
|
||||
case m: EditCaptionHistoryPubMsg => captionApp2x.handleEditCaptionHistoryPubMsg(m)
|
||||
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handleUpdateCaptionOwnerPubMsg(m)
|
||||
case m: SendCaptionHistoryReqMsg => captionApp2x.handleSendCaptionHistoryReqMsg(m)
|
||||
|
||||
// SharedNotes
|
||||
case m: GetSharedNotesPubMsg => sharedNotesApp2x.handleGetSharedNotesPubMsg(m)
|
||||
case m: SyncSharedNotePubMsg => sharedNotesApp2x.handleSyncSharedNotePubMsg(m)
|
||||
case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handleUpdateSharedNoteReqMsg(m)
|
||||
case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handleCreateSharedNoteReqMsg(m)
|
||||
case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handleDestroySharedNoteReqMsg(m)
|
||||
|
||||
// Guests
|
||||
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
|
||||
case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
|
||||
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
|
||||
case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
|
||||
// Chat
|
||||
case m: GetChatHistoryReqMsg => chatApp2x.handleGetChatHistoryReqMsg(m)
|
||||
case m: SendPublicMessagePubMsg => chatApp2x.handleSendPublicMessagePubMsg(m)
|
||||
case m: SendPrivateMessagePubMsg => chatApp2x.handleSendPrivateMessagePubMsg(m)
|
||||
case m: ClearPublicChatHistoryPubMsg => chatApp2x.handleClearPublicChatHistoryPubMsg(m)
|
||||
|
||||
// Screenshare
|
||||
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStartedVoiceConfEvtMsg(m)
|
||||
case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(m)
|
||||
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(m)
|
||||
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg(m)
|
||||
case m: GetScreenshareStatusReqMsg => screenshareApp2x.handleGetScreenshareStatusReqMsg(m)
|
||||
|
||||
case _ => log.warning("***** Cannot handle " + msg.envelope.name)
|
||||
}
|
||||
}
|
||||
|
||||
def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = {
|
||||
// sync all meetings
|
||||
handleSyncGetMeetingInfoRespMsg(liveMeeting.props)
|
||||
|
||||
// sync all users
|
||||
usersApp.handleSyncGetUsersMeetingRespMsg()
|
||||
|
||||
// sync all presentations
|
||||
presentationApp2x.handleSyncGetPresentationInfoRespMsg()
|
||||
|
||||
// TODO send all chat
|
||||
// TODO send all lock settings
|
||||
// TODO send all screen sharing info
|
||||
}
|
||||
|
||||
def handlePresenterChange(msg: AssignPresenterReqMsg): Unit = {
|
||||
// Stop poll if one is running as presenter left
|
||||
handleStopPollReqMsg(msg.header.userId)
|
||||
|
||||
// switch user presenter status for old and new presenter
|
||||
usersApp.handleAssignPresenterReqMsg(msg)
|
||||
|
||||
// TODO stop current screen sharing session (initiated by the old presenter)
|
||||
|
||||
}
|
||||
|
||||
def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
|
||||
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting="
|
||||
+ ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel) + " URL:" +
|
||||
ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel))
|
||||
|
||||
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),
|
||||
// DeskshareModel.getDesktopShareVideoWidth(liveMeeting.deskshareModel),
|
||||
// DeskshareModel.getDesktopShareVideoHeight(liveMeeting.deskshareModel), true))
|
||||
}
|
||||
}
|
||||
|
||||
def handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = {
|
||||
Users2x.findPresenter(liveMeeting.users2x) match {
|
||||
case Some(curPres) => {
|
||||
val allowed = msg.userID equals (curPres.intId)
|
||||
// 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 handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
|
||||
|
||||
}
|
||||
|
||||
def startRecordingIfAutoStart() {
|
||||
if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
|
||||
props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
|
||||
log.info("Auto start recording. meetingId={}", props.meetingProp.intId)
|
||||
MeetingStatus2x.recordingStarted(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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class MeetingActorAudit(
|
||||
}
|
||||
|
||||
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.
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingUpdate(props.meetingProp.intId)))
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package org.bigbluebutton.core.running
|
||||
|
||||
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.bus._
|
||||
import org.bigbluebutton.core.models._
|
||||
|
@ -3,6 +3,7 @@ package org.bigbluebutton.core.util
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object TimeUtil {
|
||||
|
||||
def minutesToMillis(minutes: Long): Long = {
|
||||
TimeUnit.MINUTES.toMillis(minutes)
|
||||
}
|
||||
@ -14,4 +15,7 @@ object TimeUtil {
|
||||
def minutesToSeconds(minutes: Long): Long = {
|
||||
TimeUnit.MINUTES.toSeconds(minutes)
|
||||
}
|
||||
|
||||
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
|
||||
}
|
||||
|
@ -39,9 +39,6 @@ object MeetingStatus2x {
|
||||
def recordingStarted(status: MeetingStatus2x) = status.recording = true
|
||||
def recordingStopped(status: MeetingStatus2x) = status.recording = false
|
||||
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 getVoiceRecordingFilename(status: MeetingStatus2x): String = status.voiceRecordingFilename
|
||||
def permisionsInitialized(status: MeetingStatus2x): Boolean = status.permissionsInited
|
||||
@ -57,10 +54,6 @@ object MeetingStatus2x {
|
||||
def timeNowInMinutes(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toMinutes(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 {
|
||||
@ -75,15 +68,10 @@ class MeetingStatus2x {
|
||||
private var meetingEnded = false
|
||||
private var meetingMuted = false
|
||||
|
||||
private var hasLastWebUserLeft = false
|
||||
private var lastWebUserLeftOnTimestamp: Long = 0
|
||||
|
||||
private var voiceRecordingFilename: String = ""
|
||||
|
||||
private var extension = new MeetingExtensionProp
|
||||
|
||||
private val startedOn = MeetingStatus2x.timeNowInSeconds;
|
||||
|
||||
private def startRecordingVoice() {
|
||||
recordingVoice = true
|
||||
}
|
||||
@ -115,9 +103,7 @@ class MeetingStatus2x {
|
||||
private def recordingStarted() = recording = true
|
||||
private def recordingStopped() = recording = false
|
||||
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 getVoiceRecordingFilename(): String = voiceRecordingFilename
|
||||
private def permisionsInitialized(): Boolean = permissionsInited
|
||||
|
@ -2,10 +2,11 @@ package org.bigbluebutton.core2.message.handlers
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
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.running.{ BaseMeetingActor, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
||||
trait SendTimeRemainingUpdateHdlr {
|
||||
this: BaseMeetingActor =>
|
||||
@ -13,10 +14,11 @@ trait SendTimeRemainingUpdateHdlr {
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate) {
|
||||
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate, state: MeetingState2x): MeetingState2x = {
|
||||
|
||||
if (liveMeeting.props.durationProps.duration > 0) {
|
||||
val endMeetingTime = MeetingStatus2x.startedOn(liveMeeting.status) + (liveMeeting.props.durationProps.duration * 60)
|
||||
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds
|
||||
val endMeetingTime = MeetingExpiryTracker.endMeetingTime(state)
|
||||
val timeRemaining = endMeetingTime - TimeUtil.timeNowInSeconds
|
||||
|
||||
def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
|
||||
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) {
|
||||
val endMeetingTime = BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms) +
|
||||
(BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms) * 60)
|
||||
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds
|
||||
val timeRemaining = endMeetingTime - TimeUtil.timeNowInSeconds
|
||||
|
||||
def buildBreakoutRoomsTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
|
||||
@ -53,6 +55,8 @@ trait SendTimeRemainingUpdateHdlr {
|
||||
BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms, 0)
|
||||
BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms, 0)
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
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.models.{ GuestPolicy, GuestPolicyType, GuestWaiting, GuestsWaiting }
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting }
|
||||
@ -12,7 +12,7 @@ trait SetGuestPolicyMsgHdlr {
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleSetGuestPolicyMsg(msg: SetGuestPolicyMsg): Unit = {
|
||||
def handleSetGuestPolicyMsg(msg: SetGuestPolicyCmdMsg): Unit = {
|
||||
val newPolicy = msg.body.policy.toUpperCase()
|
||||
if (GuestPolicyType.policyTypes.contains(newPolicy)) {
|
||||
val policy = GuestPolicy(newPolicy, msg.body.setBy)
|
||||
|
1
akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/DestroyMeetingSysCmdMsgHdlr.scala
Normal file → Executable file
1
akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/DestroyMeetingSysCmdMsgHdlr.scala
Normal file → Executable file
@ -3,7 +3,6 @@ package org.bigbluebutton.core2.message.handlers.meeting
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait DestroyMeetingSysCmdMsgHdlr {
|
||||
|
@ -2,24 +2,19 @@ package org.bigbluebutton.core2.message.handlers.meeting
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core.bus.IncomingEventBus
|
||||
import org.bigbluebutton.core.domain.MeetingEndReason
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting }
|
||||
|
||||
trait EndMeetingSysCmdMsgHdlr {
|
||||
trait EndMeetingSysCmdMsgHdlr extends HandlerHelpers {
|
||||
this: BaseMeetingActor =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMessageGateway
|
||||
val eventBus: IncomingEventBus
|
||||
|
||||
def handleEndMeeting(msg: EndMeetingSysCmdMsg) {
|
||||
// Broadcast users the meeting will end
|
||||
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))
|
||||
sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_FROM_API, eventBus, outGW, liveMeeting)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -227,26 +227,6 @@ object MsgBuilder {
|
||||
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 = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(BreakoutRoomEndedEvtMsg.NAME, routing)
|
||||
|
@ -9,14 +9,25 @@ import org.bigbluebutton.core.running.LiveMeeting
|
||||
trait FakeTestData {
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, vu1)
|
||||
@ -33,7 +44,7 @@ trait FakeTestData {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@ -49,10 +60,10 @@ trait FakeTestData {
|
||||
createFakeUser(liveMeeting, ruser1)
|
||||
}
|
||||
|
||||
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): Unit = {
|
||||
val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
||||
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
|
||||
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
||||
guest = regUser.guest, authed = regUser.authed, waitingForAcceptance = regUser.waitingForAcceptance,
|
||||
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL)
|
||||
Users2x.add(liveMeeting.users2x, u)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,10 @@ object FakeUserGenerator {
|
||||
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
|
||||
RandomStringGenerator.randomAlphanumericString(10) + ".png"
|
||||
|
||||
RegisteredUsers.create(userId = id, extId, name, role,
|
||||
authToken, avatarURL, guest, authed, waitingForAcceptance = true, users)
|
||||
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
||||
authToken, avatarURL, guest, authed, waitingForAcceptance = true)
|
||||
RegisteredUsers.add(users, ru)
|
||||
ru
|
||||
}
|
||||
|
||||
def createFakeVoiceUser(user: RegisteredUser, callingWith: String, muted: Boolean, talking: Boolean,
|
||||
|
@ -9,12 +9,10 @@ case class DisconnectAllConnections(scope: String) extends SystemMessage
|
||||
class MsgToClientGW(val connInvokerService: IConnectionInvokerService) {
|
||||
|
||||
def broadcastToMeeting(msg: BroadcastToMeetingMsg): Unit = {
|
||||
//println("**** MsgToClientGW broadcastToMeeting " + msg.json)
|
||||
connInvokerService.sendMessage(msg)
|
||||
}
|
||||
|
||||
def directToClient(msg: DirectToClientMsg): Unit = {
|
||||
//println("**** MsgToClientGW directToClient " + msg.json)
|
||||
connInvokerService.sendMessage(msg)
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ object GetGuestsWaitingApprovalReqMsg {
|
||||
val NAME = "GetGuestsWaitingApprovalReqMsg"
|
||||
}
|
||||
case class GetGuestsWaitingApprovalReqMsg(header: BbbClientMsgHeader,
|
||||
body: GetGuestsWaitingApprovalReqMsgBody) extends BbbCoreMsg
|
||||
body: GetGuestsWaitingApprovalReqMsgBody) extends StandardMsg
|
||||
case class GetGuestsWaitingApprovalReqMsgBody(requesterId: String)
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ object GuestsWaitingApprovedMsg {
|
||||
}
|
||||
|
||||
case class GuestsWaitingApprovedMsg(header: BbbClientMsgHeader,
|
||||
body: GuestsWaitingApprovedMsgBody) extends BbbCoreMsg
|
||||
body: GuestsWaitingApprovedMsgBody) extends StandardMsg
|
||||
|
||||
case class GuestsWaitingApprovedMsgBody(guests: Vector[GuestApprovedVO], approvedBy: String)
|
||||
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.
|
||||
*/
|
||||
object SetGuestPolicyMsg {
|
||||
val NAME = "SetGuestPolicyMsg"
|
||||
object SetGuestPolicyCmdMsg {
|
||||
val NAME = "SetGuestPolicyCmdMsg"
|
||||
}
|
||||
case class SetGuestPolicyMsg(header: BbbClientMsgHeader,
|
||||
body: SetGuestPolicyMsgBody) extends BbbCoreMsg
|
||||
case class SetGuestPolicyMsgBody(policy: String, setBy: String)
|
||||
case class SetGuestPolicyCmdMsg(header: BbbClientMsgHeader,
|
||||
body: SetGuestPolicyCmdMsgBody) extends StandardMsg
|
||||
case class SetGuestPolicyCmdMsgBody(policy: String, setBy: String)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
@ -59,7 +59,7 @@ case class MeetingEndedEvtMsgBody(meetingId: String)
|
||||
object MeetingEndingEvtMsg { val NAME = "MeetingEndingEvtMsg"}
|
||||
case class MeetingEndingEvtMsg(header: BbbClientMsgHeader,
|
||||
body: MeetingEndingEvtMsgBody) extends BbbCoreMsg
|
||||
case class MeetingEndingEvtMsgBody(meetingId: String)
|
||||
case class MeetingEndingEvtMsgBody(meetingId: String, reason: String)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -150,14 +150,14 @@ public class ConnectionInvokerService implements IConnectionInvokerService {
|
||||
}
|
||||
|
||||
private void handleCloseMeetingAllConnectionsMsg(CloseMeetingAllConnectionsMsg msg) {
|
||||
log.info("Disconnecting all clients for meeting {}", msg.meetingId);
|
||||
|
||||
IScope meetingScope = getScope(msg.meetingId);
|
||||
if (meetingScope != null) {
|
||||
Set<IConnection> conns = meetingScope.getClientConnections();
|
||||
|
||||
for (IConnection conn : conns) {
|
||||
if (conn.isConnected()) {
|
||||
String connId = (String) conn.getAttribute("INTERNAL_USER_ID");
|
||||
log.info("Disconnecting client=[{}] from meeting=[{}]", connId, msg.meetingId);
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
|
@ -13,5 +13,21 @@ package org.bigbluebutton.core.model
|
||||
this.streamId = streamId;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -85,7 +85,21 @@ package org.bigbluebutton.core.model
|
||||
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();
|
||||
|
||||
for (var i:int = 0; i < _webcams.length; i++) {
|
||||
|
@ -10,6 +10,10 @@ package org.bigbluebutton.core.model.users
|
||||
|
||||
private var _guests:ArrayCollection = new ArrayCollection();
|
||||
|
||||
public function getGuests(): Array {
|
||||
return new ArrayCollection(_guests.toArray()).toArray();
|
||||
}
|
||||
|
||||
public function add(user: GuestWaiting):void {
|
||||
_guests.addItem(user);
|
||||
}
|
||||
@ -63,6 +67,14 @@ package org.bigbluebutton.core.model.users
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function setGuestPolicy(value: String): void {
|
||||
_guestPolicy = value;
|
||||
}
|
||||
|
||||
public function getGuestPolicy(): String {
|
||||
return _guestPolicy;
|
||||
}
|
||||
|
||||
public function userJoined(vu: GuestWaiting):void {
|
||||
add(vu);
|
||||
}
|
||||
|
@ -18,62 +18,7 @@ package org.bigbluebutton.core.model.users
|
||||
|
||||
// Flag to tell that user is in the process of leaving the meeting.
|
||||
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 {
|
||||
var dest: User2x = new User2x();
|
||||
dest.intId = source.intId;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,140 +1,146 @@
|
||||
<?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
|
||||
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
|
||||
version.
|
||||
BigBlueButton is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Lesser General Public License as published by the Free Software
|
||||
Foundation; either version 2.1 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.
|
||||
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/>.
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$Id: $
|
||||
$Id: $
|
||||
-->
|
||||
|
||||
<mx:TitleWindow xmlns:mx="library://ns.adobe.com/flex/mx"
|
||||
xmlns:fx="http://ns.adobe.com/mxml/2009"
|
||||
title="{ResourceUtil.getInstance().getString('bbb.guests.title')}" showCloseButton="false" creationComplete="init()"
|
||||
x="0" y="0" layout="vertical" width="320" horizontalAlign="center"
|
||||
xmlns:mate="http://mate.asfusion.com/" >
|
||||
|
||||
<fx:Declarations>
|
||||
<mate:Listener type="{BBBEvent.ACCEPT_ALL_WAITING_GUESTS}" method="acceptAllWaitingGuests" />
|
||||
<mate:Listener type="{BBBEvent.DENY_ALL_WAITING_GUESTS}" method="denyAllWaitingGuests" />
|
||||
<mate:Listener type="{RemoveGuestFromViewEvent.REMOVE_GUEST}" receive="{remove(event.userid)}" />
|
||||
</fx:Declarations>
|
||||
|
||||
<fx:Script>
|
||||
<![CDATA[
|
||||
import mx.managers.PopUpManager;
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
import org.bigbluebutton.main.events.RemoveGuestEvent;
|
||||
import org.bigbluebutton.main.events.ResponseModeratorEvent;
|
||||
import org.bigbluebutton.main.events.RemoveGuestFromViewEvent;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
import mx.controls.Button;
|
||||
import mx.events.CloseEvent;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
|
||||
private var guestButtons:Object = new Object();
|
||||
[Bindable] private var numberOfGuests:Number = 0;
|
||||
private var dispatcher:Dispatcher = new Dispatcher();
|
||||
|
||||
public function init():void {
|
||||
//Uncomment this line to make titlewindow undraggable
|
||||
//this.isPopUp = false;
|
||||
}
|
||||
|
||||
public function refreshGuestView(listOfGuests:Object):void {
|
||||
for (var userid:String in listOfGuests) {
|
||||
if(guestButtons[userid] == null) {
|
||||
var guestItem:GuestItem = new GuestItem();
|
||||
guestItem.setUser(listOfGuests[userid], userid);
|
||||
guestListBox.addChild(guestItem);
|
||||
guestButtons[userid] = guestItem;
|
||||
numberOfGuests++;
|
||||
}
|
||||
}
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
public function sendResponseToAllGuests(resp:Boolean):void {
|
||||
removeAllGuests();
|
||||
var respCommand:ResponseModeratorEvent = new ResponseModeratorEvent(ResponseModeratorEvent.RESPONSE_ALL);
|
||||
respCommand.resp = resp;
|
||||
dispatcher.dispatchEvent(respCommand);
|
||||
}
|
||||
|
||||
public function sendResponseToAllGuestsCheckBox(resp:Boolean):void {
|
||||
if(rememberCheckBox.selected) {
|
||||
var event:BBBEvent = new BBBEvent(BBBEvent.BROADCAST_GUEST_POLICY);
|
||||
if (resp) {
|
||||
event.payload['guestPolicy'] = "ALWAYS_ACCEPT";
|
||||
} else {
|
||||
event.payload['guestPolicy'] = "ALWAYS_DENY";
|
||||
}
|
||||
dispatcher.dispatchEvent(event);
|
||||
}
|
||||
sendResponseToAllGuests(resp);
|
||||
}
|
||||
|
||||
public function acceptAllWaitingGuests(event:BBBEvent):void {
|
||||
sendResponseToAllGuests(true);
|
||||
}
|
||||
|
||||
public function denyAllWaitingGuests(event:BBBEvent):void {
|
||||
sendResponseToAllGuests(false);
|
||||
}
|
||||
|
||||
public function removeAllGuests():void {
|
||||
var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent(RemoveGuestEvent.REMOVE_ALL);
|
||||
dispatcher.dispatchEvent(removeGuestEvent);
|
||||
|
||||
closeWindow();
|
||||
}
|
||||
|
||||
public function remove(userid:String):void {
|
||||
if (guestButtons[userid] != null) {
|
||||
numberOfGuests = numberOfGuests - 1;
|
||||
guestListBox.removeChild(guestButtons[userid]);
|
||||
delete guestButtons[userid];
|
||||
|
||||
var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent();
|
||||
removeGuestEvent.userid = userid;
|
||||
dispatcher.dispatchEvent(removeGuestEvent);
|
||||
|
||||
if (!hasGuest()) {
|
||||
closeWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function hasGuest():Boolean {
|
||||
return numberOfGuests > 0;
|
||||
}
|
||||
|
||||
public function closeWindow():void {
|
||||
this.visible = false;
|
||||
PopUpManager.removePopUp(this);
|
||||
dispatchEvent(new CloseEvent(CloseEvent.CLOSE));
|
||||
}
|
||||
|
||||
]]>
|
||||
</fx:Script>
|
||||
<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" />
|
||||
|
||||
xmlns:fx="http://ns.adobe.com/mxml/2009"
|
||||
title="{ResourceUtil.getInstance().getString('bbb.guests.title')}" showCloseButton="false" creationComplete="init()"
|
||||
x="0" y="0" layout="vertical" width="320" horizontalAlign="center"
|
||||
xmlns:mate="http://mate.asfusion.com/" >
|
||||
|
||||
<fx:Declarations>
|
||||
<mate:Listener type="{BBBEvent.ACCEPT_ALL_WAITING_GUESTS}" method="acceptAllWaitingGuests" />
|
||||
<mate:Listener type="{BBBEvent.DENY_ALL_WAITING_GUESTS}" method="denyAllWaitingGuests" />
|
||||
<mate:Listener type="{RemoveGuestFromViewEvent.REMOVE_GUEST}" receive="{remove(event.userid)}" />
|
||||
</fx:Declarations>
|
||||
|
||||
<fx:Script>
|
||||
<![CDATA[
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import mx.controls.Button;
|
||||
import mx.events.CloseEvent;
|
||||
import mx.managers.PopUpManager;
|
||||
|
||||
import org.bigbluebutton.core.model.LiveMeeting;
|
||||
import org.bigbluebutton.core.model.users.GuestWaiting;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.RemoveGuestEvent;
|
||||
import org.bigbluebutton.main.events.RemoveGuestFromViewEvent;
|
||||
import org.bigbluebutton.main.events.ResponseModeratorEvent;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
private var guestButtons:Object = new Object();
|
||||
[Bindable] private var numberOfGuests:Number = 0;
|
||||
private var dispatcher:Dispatcher = new Dispatcher();
|
||||
|
||||
public function init():void {
|
||||
//Uncomment this line to make titlewindow undraggable
|
||||
//this.isPopUp = false;
|
||||
}
|
||||
|
||||
public function refreshGuestView():void {
|
||||
var _guests: Array = LiveMeeting.inst().guestsWaiting.getGuests();
|
||||
for (var i:int = 0; i < _guests.length; i++) {
|
||||
var _guest: GuestWaiting = _guests[i] as GuestWaiting;
|
||||
if(guestButtons[_guest.intId] == null) {
|
||||
var guestItem:GuestItem = new GuestItem();
|
||||
guestItem.setUser(_guest.name, _guest.intId);
|
||||
guestListBox.addChild(guestItem);
|
||||
guestButtons[_guest.intId] = guestItem;
|
||||
numberOfGuests++;
|
||||
}
|
||||
}
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
public function sendResponseToAllGuests(resp:Boolean):void {
|
||||
removeAllGuests();
|
||||
var respCommand:ResponseModeratorEvent = new ResponseModeratorEvent(ResponseModeratorEvent.RESPONSE_ALL);
|
||||
respCommand.resp = resp;
|
||||
dispatcher.dispatchEvent(respCommand);
|
||||
}
|
||||
|
||||
public function sendResponseToAllGuestsCheckBox(resp:Boolean):void {
|
||||
if(rememberCheckBox.selected) {
|
||||
var event:BBBEvent = new BBBEvent(BBBEvent.BROADCAST_GUEST_POLICY);
|
||||
if (resp) {
|
||||
event.payload['guestPolicy'] = "ALWAYS_ACCEPT";
|
||||
} else {
|
||||
event.payload['guestPolicy'] = "ALWAYS_DENY";
|
||||
}
|
||||
dispatcher.dispatchEvent(event);
|
||||
}
|
||||
sendResponseToAllGuests(resp);
|
||||
}
|
||||
|
||||
public function acceptAllWaitingGuests(event:BBBEvent):void {
|
||||
sendResponseToAllGuests(true);
|
||||
}
|
||||
|
||||
public function denyAllWaitingGuests(event:BBBEvent):void {
|
||||
sendResponseToAllGuests(false);
|
||||
}
|
||||
|
||||
public function removeAllGuests():void {
|
||||
var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent(RemoveGuestEvent.REMOVE_ALL);
|
||||
dispatcher.dispatchEvent(removeGuestEvent);
|
||||
|
||||
closeWindow();
|
||||
}
|
||||
|
||||
public function remove(userid:String):void {
|
||||
if (guestButtons[userid] != null) {
|
||||
numberOfGuests = numberOfGuests - 1;
|
||||
guestListBox.removeChild(guestButtons[userid]);
|
||||
delete guestButtons[userid];
|
||||
|
||||
var removeGuestEvent:RemoveGuestEvent = new RemoveGuestEvent();
|
||||
removeGuestEvent.userid = userid;
|
||||
dispatcher.dispatchEvent(removeGuestEvent);
|
||||
|
||||
if (!hasGuest()) {
|
||||
closeWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function hasGuest():Boolean {
|
||||
return numberOfGuests > 0;
|
||||
}
|
||||
|
||||
public function closeWindow():void {
|
||||
this.visible = false;
|
||||
PopUpManager.removePopUp(this);
|
||||
//dispatchEvent(new CloseEvent(CloseEvent.CLOSE));
|
||||
}
|
||||
|
||||
]]>
|
||||
</fx:Script>
|
||||
<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>
|
||||
|
@ -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="{ConnectionFailedEvent.MODERATOR_DENIED_ME}" method="handleLogout" />
|
||||
<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.WAITING_FOR_MODERATOR_ACCEPTANCE}" method="openWaitWindow" />
|
||||
<mate:Listener type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}" method="closeWaitWindow"/>
|
||||
</fx:Declarations>
|
||||
<fx:Script>
|
||||
<![CDATA[
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.FullScreenEvent;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.TextEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.net.navigateToURL;
|
||||
|
||||
import mx.binding.utils.ChangeWatcher;
|
||||
import mx.collections.ArrayCollection;
|
||||
import mx.controls.Alert;
|
||||
import mx.core.FlexGlobals;
|
||||
import mx.core.IFlexDisplayObject;
|
||||
import mx.core.UIComponent;
|
||||
import mx.events.FlexEvent;
|
||||
|
||||
import flexlib.mdi.effects.effectsLib.MDIVistaEffects;
|
||||
|
||||
import org.as3commons.lang.StringUtils;
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.IBbbModuleWindow;
|
||||
import org.bigbluebutton.common.events.AddUIComponentToMainCanvas;
|
||||
import org.bigbluebutton.common.events.CloseWindowEvent;
|
||||
import org.bigbluebutton.common.events.OpenWindowEvent;
|
||||
import org.bigbluebutton.common.events.ToolbarButtonEvent;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.Options;
|
||||
import org.bigbluebutton.core.PopUpUtil;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.events.LockControlEvent;
|
||||
import org.bigbluebutton.core.events.SwitchedLayoutEvent;
|
||||
import org.bigbluebutton.core.vo.LockSettingsVO;
|
||||
import org.bigbluebutton.main.events.AppVersionEvent;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.BreakoutRoomEvent;
|
||||
import org.bigbluebutton.main.events.ClientStatusEvent;
|
||||
import org.bigbluebutton.main.events.ConfigLoadedEvent;
|
||||
import org.bigbluebutton.main.events.ExitApplicationEvent;
|
||||
import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
|
||||
import org.bigbluebutton.main.events.MeetingNotFoundEvent;
|
||||
import org.bigbluebutton.main.events.ModuleLoadEvent;
|
||||
import org.bigbluebutton.main.events.NetworkStatsEvent;
|
||||
import org.bigbluebutton.main.events.RefreshGuestEvent;
|
||||
import org.bigbluebutton.main.events.ShortcutEvent;
|
||||
import org.bigbluebutton.main.model.Guest;
|
||||
import org.bigbluebutton.main.model.ImageLoader;
|
||||
import org.bigbluebutton.main.model.options.BrandingOptions;
|
||||
import org.bigbluebutton.main.model.options.BrowserVersionsOptions;
|
||||
import org.bigbluebutton.main.model.options.LanguageOptions;
|
||||
import org.bigbluebutton.main.model.options.LayoutOptions;
|
||||
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.AudioSelectionWindowEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashMicSettingsEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRTCCallEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRTCEchoTestEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRTCMediaEvent;
|
||||
import org.bigbluebutton.modules.phone.models.PhoneOptions;
|
||||
import org.bigbluebutton.modules.users.views.BreakoutRoomSettings;
|
||||
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.FullScreenEvent;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.TextEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.net.navigateToURL;
|
||||
|
||||
import mx.binding.utils.ChangeWatcher;
|
||||
import mx.collections.ArrayCollection;
|
||||
import mx.controls.Alert;
|
||||
import mx.core.FlexGlobals;
|
||||
import mx.core.IFlexDisplayObject;
|
||||
import mx.core.UIComponent;
|
||||
import mx.events.FlexEvent;
|
||||
|
||||
import flexlib.mdi.effects.effectsLib.MDIVistaEffects;
|
||||
|
||||
import org.as3commons.lang.StringUtils;
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.IBbbModuleWindow;
|
||||
import org.bigbluebutton.common.events.AddUIComponentToMainCanvas;
|
||||
import org.bigbluebutton.common.events.CloseWindowEvent;
|
||||
import org.bigbluebutton.common.events.OpenWindowEvent;
|
||||
import org.bigbluebutton.common.events.ToolbarButtonEvent;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.Options;
|
||||
import org.bigbluebutton.core.PopUpUtil;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.events.LockControlEvent;
|
||||
import org.bigbluebutton.core.events.NewGuestWaitingEvent;
|
||||
import org.bigbluebutton.core.events.SwitchedLayoutEvent;
|
||||
import org.bigbluebutton.core.vo.LockSettingsVO;
|
||||
import org.bigbluebutton.main.events.AppVersionEvent;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.BreakoutRoomEvent;
|
||||
import org.bigbluebutton.main.events.ClientStatusEvent;
|
||||
import org.bigbluebutton.main.events.ConfigLoadedEvent;
|
||||
import org.bigbluebutton.main.events.ExitApplicationEvent;
|
||||
import org.bigbluebutton.main.events.InvalidAuthTokenEvent;
|
||||
import org.bigbluebutton.main.events.MeetingNotFoundEvent;
|
||||
import org.bigbluebutton.main.events.ModuleLoadEvent;
|
||||
import org.bigbluebutton.main.events.NetworkStatsEvent;
|
||||
import org.bigbluebutton.main.events.ShortcutEvent;
|
||||
import org.bigbluebutton.main.model.Guest;
|
||||
import org.bigbluebutton.main.model.ImageLoader;
|
||||
import org.bigbluebutton.main.model.options.BrandingOptions;
|
||||
import org.bigbluebutton.main.model.options.BrowserVersionsOptions;
|
||||
import org.bigbluebutton.main.model.options.LanguageOptions;
|
||||
import org.bigbluebutton.main.model.options.LayoutOptions;
|
||||
import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.AudioSelectionWindowEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashMicSettingsEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRTCCallEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRTCEchoTestEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRTCMediaEvent;
|
||||
import org.bigbluebutton.modules.phone.models.PhoneOptions;
|
||||
import org.bigbluebutton.modules.users.views.BreakoutRoomSettings;
|
||||
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
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
|
||||
if (!UsersUtil.amIModerator() || UsersUtil.amIWaitingForAcceptance()) {
|
||||
closeGuestWindow();
|
||||
@ -507,7 +507,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
guestWindow.x = systemManager.screen.width - guestWindow.width - 20;
|
||||
guestWindow.y = 20;
|
||||
}
|
||||
guestWindow.refreshGuestView(evt.listOfGuests);
|
||||
guestWindow.refreshGuestView();
|
||||
}
|
||||
|
||||
public function removeGuestWindow(evt:BBBEvent):void {
|
||||
|
@ -24,16 +24,12 @@ package org.bigbluebutton.modules.users.events
|
||||
{
|
||||
public static const VIEW_CAMERA_EVENT:String = "VIEW_CAMERA_EVENT";
|
||||
|
||||
public var stream:String;
|
||||
public var viewedName:String;
|
||||
public var userID:String;
|
||||
|
||||
public function ViewCameraEvent(userID:String, stream:String, viewedName:String)
|
||||
public function ViewCameraEvent(userID:String)
|
||||
{
|
||||
super(VIEW_CAMERA_EVENT,true);
|
||||
this.userID = userID;
|
||||
this.stream = stream;
|
||||
this.viewedName = viewedName;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{ResponseModeratorEvent.RESPONSE_ALL}" >
|
||||
<MethodInvoker generator="{UserService}" method="responseToGuest" arguments="{event}" />
|
||||
<MethodInvoker generator="{UserService}" method="responseToAllGuest" arguments="{event}" />
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{BBBEvent.BROADCAST_GUEST_POLICY}" >
|
||||
|
@ -112,15 +112,9 @@ package org.bigbluebutton.modules.users.services
|
||||
case "GuestsWaitingForApprovalEvtMsg":
|
||||
handleGuestsWaitingForApprovalEvtMsg(message);
|
||||
break;
|
||||
case "meetingEnded":
|
||||
handleLogout(message);
|
||||
break;
|
||||
case "MeetingEndingEvtMsg":
|
||||
handleMeetingEnding(message);
|
||||
break;
|
||||
case "meetingHasEnded":
|
||||
handleMeetingHasEnded(message);
|
||||
break;
|
||||
case "meetingMuted":
|
||||
handleMeetingMuted(message);
|
||||
break;
|
||||
@ -182,10 +176,10 @@ package org.bigbluebutton.modules.users.services
|
||||
case "ScreenshareRtmpBroadcastStoppedEvtMsg":
|
||||
handleScreenshareRtmpBroadcastStoppedEvtMsg(message);
|
||||
break;
|
||||
case "get_guest_policy_reply":
|
||||
case "GetGuestPolicyRespMsg":
|
||||
handleGetGuestPolicyReply(message);
|
||||
break;
|
||||
case "guest_policy_changed":
|
||||
case "GuestPolicyChangedEvtMsg":
|
||||
handleGuestPolicyChanged(message);
|
||||
break;
|
||||
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);
|
||||
LiveMeeting.inst().guestsWaiting.add(guestWaiting);
|
||||
}
|
||||
|
||||
private function handleGuestsWaitingForApprovalEvtMsg(msg: Object): void {
|
||||
var body: Object = msg.body as Object;
|
||||
var guests: Array = body.guests as Array;
|
||||
@ -473,10 +468,6 @@ package org.bigbluebutton.modules.users.services
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private function handleMeetingHasEnded(msg: Object):void {
|
||||
LOGGER.debug("*** handleMeetingHasEnded {0} **** \n", [msg.msg]);
|
||||
}
|
||||
|
||||
private function handlePermissionsSettingsChanged(msg:Object):void {
|
||||
//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
|
||||
*/
|
||||
public function handleMeetingEnding(msg:Object):void {
|
||||
// Avoid trying to reconnect
|
||||
var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT);
|
||||
dispatcher.dispatchEvent(endMeetingEvent);
|
||||
var cancelReconnectEvent:BBBEvent = new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT);
|
||||
dispatcher.dispatchEvent(cancelReconnectEvent);
|
||||
var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.END_MEETING_EVENT);
|
||||
dispatcher.dispatchEvent(endMeetingEvent);
|
||||
}
|
||||
|
||||
public function handleAssignPresenterCallback(msg:Object):void {
|
||||
@ -670,7 +654,6 @@ package org.bigbluebutton.modules.users.services
|
||||
|
||||
var webUser: User2x = UsersUtil.getUser(userId);
|
||||
if (webUser != null) {
|
||||
webUser.streamNames.push(streamId);
|
||||
sendStreamStartedEvent(userId, webUser.name, streamId);
|
||||
}
|
||||
|
||||
@ -691,6 +674,8 @@ package org.bigbluebutton.modules.users.services
|
||||
logData.user.webcamStream = stream;
|
||||
LOGGER.info(JSON.stringify(logData));
|
||||
|
||||
LiveMeeting.inst().webcams.remove(stream);
|
||||
|
||||
sendStreamStoppedEvent(userId, stream);
|
||||
}
|
||||
|
||||
@ -699,9 +684,6 @@ package org.bigbluebutton.modules.users.services
|
||||
dispatcher.dispatchEvent(new StreamStoppedEvent(userId, streamId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private function handleBreakoutRoomsList(msg:Object):void{
|
||||
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 {
|
||||
LOGGER.debug("*** handleGuestPolicyChanged " + msg.msg + " **** \n");
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
var header: Object = msg.header as Object;
|
||||
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;
|
||||
dispatcher.dispatchEvent(policy);
|
||||
var policyEvent:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY);
|
||||
policyEvent.payload['guestPolicy'] = policy;
|
||||
dispatcher.dispatchEvent(policyEvent);
|
||||
}
|
||||
|
||||
public function handleGetGuestPolicyReply(msg:Object):void {
|
||||
LOGGER.debug("*** handleGetGuestPolicyReply " + msg.msg + " **** \n");
|
||||
var map:Object = JSON.parse(msg.msg);
|
||||
|
||||
var policy:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY);
|
||||
policy.payload['guestPolicy'] = map.guestPolicy;
|
||||
dispatcher.dispatchEvent(policy);
|
||||
var header: Object = msg.header as Object;
|
||||
var body: Object = msg.body as Object;
|
||||
var policy: String = body.policy as String;
|
||||
|
||||
LiveMeeting.inst().guestsWaiting.setGuestPolicy(policy);
|
||||
|
||||
var policyEvent:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY);
|
||||
policyEvent.payload['guestPolicy'] = policyEvent;
|
||||
dispatcher.dispatchEvent(policyEvent);
|
||||
}
|
||||
|
||||
public function handleGuestAccessDenied(msg:Object):void {
|
||||
|
@ -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.RequestBreakoutJoinURLMsgBody;
|
||||
import org.bigbluebutton.core.managers.ConnectionManager;
|
||||
import org.bigbluebutton.core.model.LiveMeeting;
|
||||
import org.bigbluebutton.core.model.users.GuestWaiting;
|
||||
|
||||
public class MessageSender {
|
||||
private static const LOGGER:ILogger = getClassLogger(MessageSender);
|
||||
@ -577,8 +579,15 @@ package org.bigbluebutton.modules.users.services
|
||||
|
||||
public function queryForGuestPolicy():void {
|
||||
LOGGER.debug("queryForGuestPolicy");
|
||||
|
||||
var message:Object = {
|
||||
header: {name: "GetGuestPolicyReqMsg", meetingId: UsersUtil.getInternalMeetingID(),
|
||||
userId: UsersUtil.getMyUserID()},
|
||||
body: {requestedBy: UsersUtil.getMyUserID()}
|
||||
};
|
||||
|
||||
var _nc:ConnectionManager = BBB.initConnectionManager();
|
||||
_nc.sendMessage("participants.getGuestPolicy",
|
||||
_nc.sendMessage2x(
|
||||
function(result:String):void { // On successful result
|
||||
LOGGER.debug(result);
|
||||
},
|
||||
@ -587,14 +596,15 @@ package org.bigbluebutton.modules.users.services
|
||||
logData.tags = ["apps"];
|
||||
logData.message = "Error occured query guest policy.";
|
||||
LOGGER.info(JSON.stringify(logData));
|
||||
}
|
||||
},
|
||||
JSON.stringify(message)
|
||||
);
|
||||
}
|
||||
|
||||
public function setGuestPolicy(policy:String):void {
|
||||
LOGGER.debug("setGuestPolicy - new policy:[" + policy + "]");
|
||||
var message:Object = {
|
||||
header: {name: "SetGuestPolicyMsg", meetingId: UsersUtil.getInternalMeetingID(),
|
||||
header: {name: "SetGuestPolicyCmdMsg", meetingId: UsersUtil.getInternalMeetingID(),
|
||||
userId: 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 {
|
||||
LOGGER.debug("responseToGuest - userId:[" + userId + "] response:[" + response + "]");
|
||||
|
||||
var message:Object = new Object();
|
||||
message["userId"] = userId;
|
||||
message["response"] = response;
|
||||
var _guests: Array = new Array();
|
||||
_guests.push({guest: userId, 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.sendMessage("participants.responseToGuest",
|
||||
_nc.sendMessage2x(
|
||||
function(result:String):void { // On successful result
|
||||
LOGGER.debug(result);
|
||||
},
|
||||
@ -632,12 +647,38 @@ package org.bigbluebutton.modules.users.services
|
||||
logData.message = "Error occured response guest.";
|
||||
LOGGER.info(JSON.stringify(logData));
|
||||
},
|
||||
message
|
||||
JSON.stringify(message)
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +142,7 @@
|
||||
}
|
||||
|
||||
private function viewCamera():void {
|
||||
dispatchEvent(new ViewCameraEvent(data.userId, data.streamName, data.name));
|
||||
dispatchEvent(new ViewCameraEvent(data.userId));
|
||||
}
|
||||
|
||||
private function kickUser():void{
|
||||
|
@ -52,69 +52,77 @@
|
||||
<mate:Listener type="{UserLeftEvent.LEFT}" method="handleUserLeftEvent" />
|
||||
<mate:Listener type="{UserEmojiChangedEvent.USER_EMOJI_CHANGED}" method="handleUserEmojiChangedEvent" />
|
||||
<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:Script>
|
||||
<![CDATA[
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.net.navigateToURL;
|
||||
|
||||
import mx.binding.utils.BindingUtils;
|
||||
import mx.collections.ArrayCollection;
|
||||
import mx.controls.Alert;
|
||||
import mx.controls.Menu;
|
||||
import mx.controls.dataGridClasses.DataGridColumn;
|
||||
import mx.controls.listClasses.IListItemRenderer;
|
||||
import mx.core.FlexGlobals;
|
||||
import mx.core.mx_internal;
|
||||
import mx.events.CloseEvent;
|
||||
import mx.events.CollectionEvent;
|
||||
import mx.events.ListEvent;
|
||||
import mx.events.MenuEvent;
|
||||
import mx.managers.PopUpManager;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.IBbbModuleWindow;
|
||||
import org.bigbluebutton.common.Role;
|
||||
import org.bigbluebutton.common.events.LocaleChangeEvent;
|
||||
import org.bigbluebutton.core.EventConstants;
|
||||
import org.bigbluebutton.core.KeyboardUtil;
|
||||
import org.bigbluebutton.core.PopUpUtil;
|
||||
import org.bigbluebutton.core.TimerUtil;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.events.BreakoutRoomsListUpdatedEvent;
|
||||
import org.bigbluebutton.core.events.CoreEvent;
|
||||
import org.bigbluebutton.core.events.LockControlEvent;
|
||||
import org.bigbluebutton.core.events.UserEmojiChangedEvent;
|
||||
import org.bigbluebutton.core.events.UserStatusChangedEvent;
|
||||
import org.bigbluebutton.core.events.VoiceConfEvent;
|
||||
import org.bigbluebutton.core.model.LiveMeeting;
|
||||
import org.bigbluebutton.core.model.users.User2x;
|
||||
import org.bigbluebutton.core.vo.LockSettingsVO;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.BreakoutRoomEvent;
|
||||
import org.bigbluebutton.main.events.ShortcutEvent;
|
||||
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
|
||||
import org.bigbluebutton.main.events.UserJoinedEvent;
|
||||
import org.bigbluebutton.main.events.UserLeftEvent;
|
||||
import org.bigbluebutton.main.model.users.BreakoutRoom;
|
||||
import org.bigbluebutton.main.model.users.events.ChangeMyRole;
|
||||
import org.bigbluebutton.main.model.users.events.EmojiStatusEvent;
|
||||
import org.bigbluebutton.main.model.users.events.KickUserEvent;
|
||||
import org.bigbluebutton.main.model.users.events.RoleChangeEvent;
|
||||
import org.bigbluebutton.main.views.MainCanvas;
|
||||
import org.bigbluebutton.main.views.WellPositionedMenu;
|
||||
import org.bigbluebutton.modules.phone.events.LeaveVoiceConferenceCommand;
|
||||
import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
|
||||
import org.bigbluebutton.modules.users.events.UsersRollEvent;
|
||||
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;
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.net.navigateToURL;
|
||||
|
||||
import mx.binding.utils.BindingUtils;
|
||||
import mx.collections.ArrayCollection;
|
||||
import mx.controls.Alert;
|
||||
import mx.controls.Menu;
|
||||
import mx.controls.dataGridClasses.DataGridColumn;
|
||||
import mx.controls.listClasses.IListItemRenderer;
|
||||
import mx.core.FlexGlobals;
|
||||
import mx.core.mx_internal;
|
||||
import mx.events.CloseEvent;
|
||||
import mx.events.CollectionEvent;
|
||||
import mx.events.ListEvent;
|
||||
import mx.events.MenuEvent;
|
||||
import mx.managers.PopUpManager;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.IBbbModuleWindow;
|
||||
import org.bigbluebutton.common.Role;
|
||||
import org.bigbluebutton.common.events.LocaleChangeEvent;
|
||||
import org.bigbluebutton.core.EventConstants;
|
||||
import org.bigbluebutton.core.KeyboardUtil;
|
||||
import org.bigbluebutton.core.PopUpUtil;
|
||||
import org.bigbluebutton.core.TimerUtil;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.events.BreakoutRoomsListUpdatedEvent;
|
||||
import org.bigbluebutton.core.events.CoreEvent;
|
||||
import org.bigbluebutton.core.events.LockControlEvent;
|
||||
import org.bigbluebutton.core.events.UserEmojiChangedEvent;
|
||||
import org.bigbluebutton.core.events.UserStatusChangedEvent;
|
||||
import org.bigbluebutton.core.events.VoiceConfEvent;
|
||||
import org.bigbluebutton.core.model.LiveMeeting;
|
||||
import org.bigbluebutton.core.model.users.User2x;
|
||||
import org.bigbluebutton.core.vo.LockSettingsVO;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.BreakoutRoomEvent;
|
||||
import org.bigbluebutton.main.events.ShortcutEvent;
|
||||
import org.bigbluebutton.main.events.StartedViewingWebcamEvent;
|
||||
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
|
||||
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
|
||||
import org.bigbluebutton.main.events.UserJoinedEvent;
|
||||
import org.bigbluebutton.main.events.UserLeftEvent;
|
||||
import org.bigbluebutton.main.model.users.BreakoutRoom;
|
||||
import org.bigbluebutton.main.model.users.events.ChangeMyRole;
|
||||
import org.bigbluebutton.main.model.users.events.EmojiStatusEvent;
|
||||
import org.bigbluebutton.main.model.users.events.KickUserEvent;
|
||||
import org.bigbluebutton.main.model.users.events.RoleChangeEvent;
|
||||
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
|
||||
import org.bigbluebutton.main.model.users.events.StreamStoppedEvent;
|
||||
import org.bigbluebutton.main.views.MainCanvas;
|
||||
import org.bigbluebutton.main.views.WellPositionedMenu;
|
||||
import org.bigbluebutton.modules.phone.events.LeaveVoiceConferenceCommand;
|
||||
import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
|
||||
import org.bigbluebutton.modules.users.events.UsersRollEvent;
|
||||
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);
|
||||
|
||||
@ -196,7 +204,22 @@
|
||||
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 {
|
||||
handler.handleUserJoinedEvent(event);
|
||||
}
|
||||
|
@ -8,7 +8,11 @@ package org.bigbluebutton.modules.users.views
|
||||
import org.bigbluebutton.core.model.LiveMeeting;
|
||||
import org.bigbluebutton.core.model.users.User2x;
|
||||
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.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.BBBVoiceUser2x;
|
||||
|
||||
@ -86,15 +90,14 @@ package org.bigbluebutton.modules.users.views
|
||||
users.refresh();
|
||||
}
|
||||
|
||||
private function getWebcamStreamsForUser(userId: String): String {
|
||||
private function getWebcamStreamsForUser(userId: String): Array {
|
||||
var streamIds: Array = LiveMeeting.inst().webcams.getStreamIdsForUser(userId);
|
||||
var streams:String = "";
|
||||
for each(var stream:String in streamIds) {
|
||||
streams = streams + stream + "|";
|
||||
}
|
||||
//Remove last |
|
||||
streams = streams.slice(0, streams.length-1);
|
||||
return streams;
|
||||
return streamIds;
|
||||
}
|
||||
|
||||
private function getWebcamStreamsViewingForUser(userId: String): Array {
|
||||
var streamIds: Array = LiveMeeting.inst().webcams.getStreamIdsIAmViewingForUser(userId);
|
||||
return streamIds;
|
||||
}
|
||||
|
||||
private function addUser(users: ArrayCollection, user: User2x):void {
|
||||
@ -107,7 +110,8 @@ package org.bigbluebutton.modules.users.views
|
||||
buser.locked = user.locked;
|
||||
buser.emojiStatus = user.emoji;
|
||||
buser.presenter = user.presenter;
|
||||
buser.streamName = getWebcamStreamsForUser(buser.userId);
|
||||
buser.streams = getWebcamStreamsForUser(buser.userId);
|
||||
buser.viewedStream = getWebcamStreamsViewingForUser(buser.userId);
|
||||
|
||||
buser.inVoiceConf = false;
|
||||
|
||||
@ -131,7 +135,6 @@ package org.bigbluebutton.modules.users.views
|
||||
var user: User2x = UsersUtil.getUser(event.userID);
|
||||
if (user != null) {
|
||||
addUser(users, user);
|
||||
trace("!!!!!!!!!!!!!!!********* " + user.name + " " + user.presenter + " ********!!!!!!!!");
|
||||
users.refresh();
|
||||
}
|
||||
}
|
||||
@ -153,8 +156,6 @@ package org.bigbluebutton.modules.users.views
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function handleUserJoinedVoiceConfEvent(userId: String):void {
|
||||
var webUser: BBBUser2x = findUser(userId);
|
||||
if (webUser != null) {
|
||||
@ -258,6 +259,32 @@ package org.bigbluebutton.modules.users.views
|
||||
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 {
|
||||
for (var i: int = 0; i < users.length; i++) {
|
||||
var user: BBBUser2x = users[i] as BBBUser2x;
|
||||
|
@ -19,15 +19,17 @@
|
||||
package org.bigbluebutton.modules.users.views.model
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
import org.as3commons.lang.ArrayUtils;
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
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.StatusCollection;
|
||||
import org.bigbluebutton.main.model.users.events.StreamStartedEvent;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
public class BBBUser2x {
|
||||
private static const LOGGER:ILogger = getClassLogger(BBBUser2x);
|
||||
@ -48,7 +50,7 @@ package org.bigbluebutton.modules.users.views.model
|
||||
|
||||
[Bindable]
|
||||
public function get hasStream():Boolean {
|
||||
return streamNames.length > 0;
|
||||
return _streamNames.length > 0;
|
||||
}
|
||||
public function set hasStream(s:Boolean):void {
|
||||
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]
|
||||
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;
|
||||
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]
|
||||
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;
|
||||
public function get streams():Array {
|
||||
return _streamNames;
|
||||
}
|
||||
|
||||
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 {
|
||||
if(streamNames) {
|
||||
var streamNamesList:Array = streamNames.split("|");
|
||||
for each(var streamName:String in streamNamesList) {
|
||||
sharedWebcam(streamName);
|
||||
}
|
||||
public function set streams(streamIds:Array):void {
|
||||
if (streamIds != null) {
|
||||
_streamNames = streamIds;
|
||||
// for each(var streamId:String in _streamNames) {
|
||||
// sharedWebcam(streamId);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,19 +222,12 @@ package org.bigbluebutton.modules.users.views.model
|
||||
|
||||
public function sharedWebcam(stream: String):void {
|
||||
if(stream && stream != "" && !hasThisStream(stream)) {
|
||||
streamNames.push(stream);
|
||||
sendStreamStartedEvent(stream);
|
||||
}
|
||||
buildStatus();
|
||||
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 {
|
||||
this.presenter = presenter;
|
||||
buildStatus();
|
||||
@ -270,18 +243,6 @@ package org.bigbluebutton.modules.users.views.model
|
||||
case "presenter":
|
||||
presenter=(status.value.toString().toUpperCase() == "TRUE") ? true : false;
|
||||
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
|
||||
case "emojiStatus":
|
||||
emojiStatus = status.value.toString();
|
||||
|
@ -88,7 +88,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<EventHandlers type="{StreamStartedEvent.STREAM_STARTED}">
|
||||
<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 type="{StreamStoppedEvent.STREAM_STOPPED}">
|
||||
@ -96,7 +96,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
</EventHandlers>
|
||||
|
||||
<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 type="{UserJoinedEvent.JOINED}">
|
||||
|
@ -114,11 +114,10 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
_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]);
|
||||
|
||||
if (!_ready) return;
|
||||
LOGGER.debug("VideoEventMapDelegate:: [{0}] Viewing [{1} stream [{2}]", [me, userID, stream]);
|
||||
if (! UsersUtil.isMe(userID)) {
|
||||
openViewWindowFor(userID);
|
||||
}
|
||||
|
@ -11,13 +11,15 @@ package org.bigbluebutton.modules.videoconf.views
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.model.VideoProfile;
|
||||
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.StartedViewingWebcamEvent;
|
||||
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
|
||||
import org.bigbluebutton.main.views.VideoWithWarnings;
|
||||
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 {
|
||||
private static const LOGGER:ILogger = getClassLogger(UserVideo);
|
||||
@ -125,13 +127,29 @@ package org.bigbluebutton.modules.videoconf.views
|
||||
}
|
||||
|
||||
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();
|
||||
stopEvent.webcamUserID = user.intId;
|
||||
stopEvent.streamName = _streamName;
|
||||
_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 {
|
||||
var e:StopBroadcastEvent = new StopBroadcastEvent();
|
||||
e.stream = _streamName;
|
||||
@ -183,7 +201,8 @@ package org.bigbluebutton.modules.videoconf.views
|
||||
|
||||
_ns.play(streamName);
|
||||
|
||||
user.addViewingStream(streamName);
|
||||
startedViewing();
|
||||
|
||||
invalidateDisplayList();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user