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

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

View File

@ -59,10 +59,11 @@ class BigBlueButtonActor(
}
def receive = {
// Internal messages
case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg)
// 2x messages
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
case msg: ValidateAuthToken => handleValidateAuthToken(msg)
case _ => // do nothing
}
@ -72,7 +73,6 @@ class BigBlueButtonActor(
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)
}
}
@ -125,59 +125,17 @@ 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
// Delay sending DisconnectAllUsers because of RTMPT connection being dropped before UserEject message arrives to the client
context.system.scheduler.scheduleOnce(Duration.create(2500, TimeUnit.MILLISECONDS)) {
// Disconnect all clients
val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.body.meetingId)
outGW.send(disconnectEvnt)
log.info("Destroyed meetingId={}", msg.body.meetingId)
val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.body.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)
@ -187,6 +145,18 @@ class BigBlueButtonActor(
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.meetingId)
outGW.send(disconnectEvnt)
log.info("Destroyed meetingId={}", msg.meetingId)
val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.meetingId)
outGW.send(destroyedEvent)
// Stop the meeting actor.
context.stop(m.actorRef)
}

View File

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

View File

@ -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

View File

@ -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 =>

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -1,9 +1,9 @@
package org.bigbluebutton.core2.message.handlers.users
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.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 =>

View File

@ -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)
}
}

View File

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

View File

@ -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)
}
}
}

View File

@ -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 =>
@ -13,13 +14,30 @@ trait MeetingActivityResponseCmdMsgHdlr {
def handleMeetingActivityResponseCmdMsg(
msg: MeetingActivityResponseCmdMsg,
tracker: MeetingInactivityTracker
): MeetingInactivityTracker = {
MeetingInactivityTrackerHelper.processMeetingActivityResponse(
props = liveMeeting.props,
outGW,
msg,
tracker
)
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)
}
}

View File

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

View File

@ -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)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,8 +2,10 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.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
}
}

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -0,0 +1,19 @@
package org.bigbluebutton.core.domain
object MeetingState2x {
}
case class MeetingState2x(
inactivityTracker: MeetingInactivityTracker,
expiryTracker: MeetingExpiryTracker
)
object MeetingEndReason {
val ENDED_FROM_API = "ENDED_FROM_API"
val ENDED_DUE_TO_INACTIVITY = "ENDED_DUE_TO_ACTIVITY"
val ENDED_WHEN_NOT_JOINED = "ENDED_WHEN_NOT_JOINED"
val ENDED_WHEN_LAST_USER_LEFT = "ENDED_WHEN_LAST_USER_LEFT"
val ENDED_AFTER_USER_LOGGED_OUT = "ENDED_AFTER_USER_LOGGED_OUT"
val ENDED_AFTER_EXCEEDING_DURATION = "ENDED_AFTER_USER_LOGGED_OUT"
}

View File

@ -6,10 +6,8 @@ import com.softwaremill.quicklens._
object RegisteredUsers {
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)
}

View File

@ -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)

View File

@ -1,91 +1,16 @@
package org.bigbluebutton.core.running
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.RecordingStatusChanged
import org.bigbluebutton.core.{ MessageRecorder, OutMessageGateway }
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 {
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)
}
trait HandlerHelpers extends SystemConfiguration {
def sendAllWebcamStreams(outGW: OutMessageGateway, requesterId: String, webcams: Webcams, meetingId: String): Unit = {
val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams)
@ -100,24 +25,12 @@ trait HandlerHelpers {
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 {
def userJoinMeeting(outGW: OutMessageGateway, authToken: String,
liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
val nu = for {
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers)
} yield {
val userState = UserState(
UserState(
intId = regUser.id,
extId = regUser.externId,
name = regUser.name,
@ -130,14 +43,24 @@ trait HandlerHelpers {
locked = false,
avatar = regUser.avatarURL
)
}
Users2x.add(liveMeeting.users2x, userState)
nu match {
case Some(newUser) =>
Users2x.add(liveMeeting.users2x, newUser)
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, userState)
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser)
Sender.send(outGW, event)
MessageRecorder.record(outGW, liveMeeting.props.recordProp.record, event.core)
startRecordingIfAutoStart2x(liveMeeting)
if (!state.expiryTracker.userHasJoined) {
MeetingExpiryTracker.setUserHasJoined(state)
} else {
state
}
case None =>
state
}
}
@ -165,4 +88,105 @@ trait HandlerHelpers {
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)
}
}

View File

@ -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)
}

View File

@ -2,8 +2,8 @@ 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.apps.users._
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x }
import org.bigbluebutton.core.util.TimeUtil
//import java.util.concurrent.TimeUnit
@ -31,7 +31,7 @@ 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
@ -102,19 +102,29 @@ class MeetingActor(
val captionApp2x = new CaptionApp2x(liveMeeting, outGW)
val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW)
val chatApp2x = new ChatApp2x(liveMeeting, outGW)
val usersApp = new UsersApp(liveMeeting, outGW)
val usersApp = new UsersApp(liveMeeting, outGW, eventBus)
var inactivityTracker = new MeetingInactivityTracker(
liveMeeting.props.durationProps.maxInactivityTimeoutMinutes,
liveMeeting.props.durationProps.warnMinutesBeforeMax,
TimeUtil.millisToMinutes(System.currentTimeMillis()), false, 0L
val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW, eventBus)
val inactivityTracker = new MeetingInactivityTracker(
props.durationProps.maxInactivityTimeoutMinutes,
props.durationProps.warnMinutesBeforeMax,
lastActivityTimestamp = TimeUtil.timeNowInSeconds(),
warningSent = false,
warningSentOnTimestamp = 0L
)
var expiryTracker = new MeetingExpiryTracker(
TimeUtil.millisToMinutes(System.currentTimeMillis()),
false, 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)
@ -137,35 +147,34 @@ class MeetingActor(
//=======================================
// old messages
case msg: MonitorNumberOfUsers => handleMonitorNumberOfUsers(msg)
case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg)
case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg)
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
case msg: SendTimeRemainingUpdate => handleSendTimeRemainingUpdate(msg)
case msg: SendTimeRemainingUpdate => state = handleSendTimeRemainingUpdate(msg, state)
// 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)
state = MeetingInactivityTracker.updateLastActivityTimestamp(state, TimeUtil.timeNowInSeconds())
msg.core match {
// Users
case m: ValidateAuthTokenReqMsg => usersApp.handleValidateAuthTokenReqMsg(m)
case m: UserJoinMeetingReqMsg => handleUserJoinMeetingReqMsg(m)
case m: UserLeaveReqMsg => handleUserLeaveReqMsg(m)
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 => inactivityTracker = usersApp.handleMeetingActivityResponseCmdMsg(m, inactivityTracker)
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)
@ -247,9 +256,9 @@ class MeetingActor(
// Guests
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
case m: SetGuestPolicyMsg => handleSetGuestPolicyMsg(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)
@ -308,18 +317,6 @@ class MeetingActor(
}
}
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) => {
@ -330,46 +327,9 @@ class MeetingActor(
}
}
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 handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) {
state = expiryTrackerHelper.processMeetingInactivityAudit(state)
state = expiryTrackerHelper.processMeetingExpiryAudit(state)
}
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {

View File

@ -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)))

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.running
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._

View File

@ -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())
}

View File

@ -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

View File

@ -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
}
}

View File

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

View File

@ -1,6 +1,6 @@
package org.bigbluebutton.core2.message.handlers.guests
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)

View 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 {

View File

@ -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)
}
}

View File

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

View File

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

View File

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

View File

@ -227,26 +227,6 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
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)

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
/**

View File

@ -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();
}
}

View File

@ -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);
}
}
}
}

View File

@ -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++) {

View File

@ -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);
}

View File

@ -19,61 +19,6 @@ 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;

View File

@ -0,0 +1,38 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.main.events
{
import flash.events.Event;
public class StartedViewingWebcamEvent extends Event
{
public static const STARTED_VIEWING_WEBCAM:String = "started viewing webcam event";
// The userID of the webcam being viewed.
public var webcamUserID:String;
// The streamName of the user
public var streamName:String;
public function StartedViewingWebcamEvent(bubbles:Boolean=true, cancelable:Boolean=false)
{
super(STARTED_VIEWING_WEBCAM, bubbles, cancelable);
}
}
}

View File

@ -1,23 +1,23 @@
<?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"
@ -34,15 +34,19 @@
<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 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;
@ -53,13 +57,15 @@
//this.isPopUp = false;
}
public function refreshGuestView(listOfGuests:Object):void {
for (var userid:String in listOfGuests) {
if(guestButtons[userid] == null) {
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(listOfGuests[userid], userid);
guestItem.setUser(_guest.name, _guest.intId);
guestListBox.addChild(guestItem);
guestButtons[userid] = guestItem;
guestButtons[_guest.intId] = guestItem;
numberOfGuests++;
}
}
@ -124,7 +130,7 @@
public function closeWindow():void {
this.visible = false;
PopUpManager.removePopUp(this);
dispatchEvent(new CloseEvent(CloseEvent.CLOSE));
//dispatchEvent(new CloseEvent(CloseEvent.CLOSE));
}
]]>

View File

@ -78,7 +78,7 @@ 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"/>
@ -117,6 +117,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
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;
@ -129,7 +130,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
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;
@ -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 {

View File

@ -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;
}
}

View File

@ -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}" >

View File

@ -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;
@ -474,10 +469,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]);
var map:Object = JSON.parse(msg.msg);
@ -566,21 +557,14 @@ 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);
var cancelReconnectEvent:BBBEvent = new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT);
dispatcher.dispatchEvent(cancelReconnectEvent);
var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.END_MEETING_EVENT);
dispatcher.dispatchEvent(endMeetingEvent);
}
@ -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);
}
@ -700,9 +685,6 @@ package org.bigbluebutton.modules.users.services
}
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 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);
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 {

View File

@ -37,6 +37,8 @@ package org.bigbluebutton.modules.users.services
import org.bigbluebutton.core.connection.messages.breakoutrooms.RequestBreakoutJoinURLMsg;
import org.bigbluebutton.core.connection.messages.breakoutrooms.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)
);
}
}
}

View File

@ -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{

View File

@ -52,6 +52,10 @@
<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>
@ -97,6 +101,8 @@
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;
@ -105,6 +111,8 @@
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;
@ -196,6 +204,21 @@
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);

View File

@ -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 + "|";
return streamIds;
}
//Remove last |
streams = streams.slice(0, streams.length-1);
return streams;
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;

View File

@ -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();

View File

@ -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}">

View File

@ -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);
}

View File

@ -11,9 +11,11 @@ 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;
@ -125,11 +127,27 @@ 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 {
@ -183,7 +201,8 @@ package org.bigbluebutton.modules.videoconf.views
_ns.play(streamName);
user.addViewingStream(streamName);
startedViewing();
invalidateDisplayList();
}