From 36601345c227d640d4bf9d395134bede2fdd4850 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 24 Jun 2015 20:32:00 +0000 Subject: [PATCH] - stop poll if presenter leaves or presentation page changed --- .../core/BigBlueButtonInGW.scala | 4 +- .../org/bigbluebutton/core/MeetingActor.scala | 19 ++- .../org/bigbluebutton/core/MeetingModel.scala | 5 + .../bigbluebutton/core/api/InMessages.scala | 4 +- .../bigbluebutton/core/api/OutMessages.scala | 2 +- .../bigbluebutton/core/apps/LayoutApp.scala | 4 +- .../org/bigbluebutton/core/apps/PollApp.scala | 69 +++++----- .../bigbluebutton/core/apps/PollModel.scala | 71 +---------- .../core/apps/PresentationApp.scala | 15 ++- .../core/apps/PresentationModel.scala | 10 ++ .../bigbluebutton/core/apps/UsersApp.scala | 119 +++++++++--------- 11 files changed, 133 insertions(+), 189 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala index 766711b344..e264573aeb 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala @@ -439,11 +439,11 @@ class BigBlueButtonInGW(val system: ActorSystem, outGW: MessageOutGateway, voice } def startPoll(meetingId: String, requesterId: String, pollId: String, pollType: String) { - bbbActor ! new StartPollRequest(meetingId, requesterId, pollId, pollType) + bbbActor ! new StartPollRequest(meetingId, requesterId, pollType) } def stopPoll(meetingId: String, userId: String, pollId: String) { - bbbActor ! new StopPollRequest(meetingId, userId, pollId) + bbbActor ! new StopPollRequest(meetingId, userId) } def showPollResult(meetingId: String, requesterId: String, pollId: String, show: java.lang.Boolean) { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala index eb999b52f0..936cc8d7fe 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala @@ -10,11 +10,7 @@ import org.bigbluebutton.core.util._ import scala.concurrent.duration._ import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp } import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel } - -case object StopMeetingActor -case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean, - voiceBridge: String, duration: Long, autoStartRecording: Boolean, allowStartStopRecording: Boolean, - moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) +import org.bigbluebutton.core.apps.PresentationModel object MeetingActor { def props(mProps: MeetingProperties, outGW: MessageOutGateway): Props = @@ -29,9 +25,10 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: MessageOutGateway) val chatModel = new ChatModel() val layoutModel = new LayoutModel() val meetingModel = new MeetingModel() - val users = new UsersModel() + val usersModel = new UsersModel() val pollModel = new PollModel() val wbModel = new WhiteboardModel() + val presModel = new PresentationModel() import context.dispatcher context.system.scheduler.schedule(2 seconds, 5 seconds, self, "MonitorNumberOfWebUsers") @@ -197,13 +194,13 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: MessageOutGateway) } def webUserJoined() { - if (users.numWebUsers > 0) { + if (usersModel.numWebUsers > 0) { meetingModel.resetLastWebUserLeftOn() } } def startRecordingIfAutoStart() { - if (mProps.recorded && !meetingModel.isRecording() && mProps.autoStartRecording && users.numWebUsers == 1) { + if (mProps.recorded && !meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 1) { log.info("Auto start recording for meeting=[" + mProps.meetingID + "]") meetingModel.recordingStarted() outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording())) @@ -211,7 +208,7 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: MessageOutGateway) } def stopAutoStartedRecording() { - if (mProps.recorded && meetingModel.isRecording() && mProps.autoStartRecording && users.numWebUsers == 0) { + if (mProps.recorded && meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 0) { log.info("Last web user left. Auto stopping recording for meeting=[{}", mProps.meetingID) meetingModel.recordingStopped() outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording())) @@ -219,7 +216,7 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: MessageOutGateway) } def startCheckingIfWeNeedToEndVoiceConf() { - if (users.numWebUsers == 0) { + if (usersModel.numWebUsers == 0) { meetingModel.lastWebUserLeft() log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]") } @@ -227,7 +224,7 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: MessageOutGateway) def handleMonitorNumberOfWebUsers() { println("BACK TIMER") - if (users.numWebUsers == 0 && meetingModel.lastWebUserLeftOn > 0) { + if (usersModel.numWebUsers == 0 && meetingModel.lastWebUserLeftOn > 0) { if (timeNowInMinutes - meetingModel.lastWebUserLeftOn > 2) { log.info("MonitorNumberOfWebUsers empty for meeting [" + mProps.meetingID + "]. Ejecting all users from voice.") outGW.send(new EjectAllVoiceUsers(mProps.meetingID, mProps.recorded, mProps.voiceBridge)) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala index b2e40d868c..43a905f8b5 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala @@ -3,6 +3,11 @@ package org.bigbluebutton.core import org.bigbluebutton.core.api.Permissions import java.util.concurrent.TimeUnit +case object StopMeetingActor +case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean, + voiceBridge: String, duration: Long, autoStartRecording: Boolean, allowStartStopRecording: Boolean, + moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) + class MeetingModel { private var audioSettingsInited = false private var permissionsInited = false diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala index a853a7b797..f8a80465cb 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala @@ -78,8 +78,8 @@ case class PresentationConversionCompleted(meetingID: String, messageKey: String // Polling //case class CreatePollRequest(meetingID: String, requesterId: String, pollId: String, pollType: String) extends InMessage -case class StartPollRequest(meetingID: String, requesterId: String, pollId: String, pollType: String) extends InMessage -case class StopPollRequest(meetingID: String, requesterId: String, pollId: String) extends InMessage +case class StartPollRequest(meetingID: String, requesterId: String, pollType: String) extends InMessage +case class StopPollRequest(meetingID: String, requesterId: String) extends InMessage case class ShowPollResultRequest(meetingID: String, requesterId: String, pollId: String) extends InMessage case class HidePollResultRequest(meetingID: String, requesterId: String, pollId: String) extends InMessage case class RespondToPollRequest(meetingID: String, requesterId: String, pollId: String, questionId: Int, answerId: Int) extends InMessage diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala index 265c989187..540a214eaf 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala @@ -112,7 +112,7 @@ case class PageChanged(meetingID: String, page: Page) extends IOutMessage case class PollStartedMessage(meetingID: String, recorded: Boolean, requesterId: String, pollId: String, poll: SimplePollOutVO) extends IOutMessage case class StartPollReplyMessage(meetingID: String, recorded: Boolean, result: RequestResult, requesterId: String, pollId: String) extends IOutMessage case class PollStoppedMessage(meetingID: String, recorded: Boolean, requesterId: String, pollId: String) extends IOutMessage -case class StopPollReplyMessage(meetingID: String, recorded: Boolean, result: RequestResult, requesterId: String, pollId: String) extends IOutMessage +case class StopPollReplyMessage(meetingID: String, recorded: Boolean, result: RequestResult, requesterId: String) extends IOutMessage case class PollShowResultMessage(meetingID: String, recorded: Boolean, requesterId: String, pollId: String, poll: SimplePollResultOutVO) extends IOutMessage case class ShowPollResultReplyMessage(meetingID: String, recorded: Boolean, result: RequestResult, requesterId: String, pollId: String) extends IOutMessage case class PollHideResultMessage(meetingID: String, recorded: Boolean, requesterId: String, pollId: String) extends IOutMessage diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/LayoutApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/LayoutApp.scala index ac2e5fa722..6398bc0e9b 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/LayoutApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/LayoutApp.scala @@ -45,14 +45,14 @@ trait LayoutApp { def affectedUsers(): Array[UserVO] = { if (layoutModel.doesLayoutApplyToViewersOnly()) { val au = ArrayBuffer[UserVO]() - users.getUsers foreach { u => + usersModel.getUsers foreach { u => if (!u.presenter && u.role != Role.MODERATOR) { au += u } } au.toArray } else { - users.getUsers + usersModel.getUsers } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala index 31df02a49c..f132a45489 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala @@ -15,7 +15,13 @@ trait PollApp { } def handleGetCurrentPollRequest(msg: GetCurrentPollRequest) { - pollModel.getCurrentPoll() match { + + val poll = for { + page <- presModel.getCurrentPage() + curPoll <- pollModel.getRunningPollThatStartsWith(page.id) + } yield curPoll + + poll match { case Some(p) => { if (p.started && p.stopped && p.showResult) { outGW.send(new GetCurrentPollReplyMessage(mProps.meetingID, mProps.recorded, msg.requesterId, true, Some(p))) @@ -68,61 +74,44 @@ trait PollApp { } def handleStopPollRequest(msg: StopPollRequest) { - pollModel.getPoll(msg.pollId) match { + val cpoll = for { + page <- presModel.getCurrentPage() + curPoll <- pollModel.getRunningPollThatStartsWith(page.id) + } yield curPoll + + cpoll match { case Some(poll) => { pollModel.stopPoll(poll.id) - outGW.send(new PollStoppedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId)) + outGW.send(new PollStoppedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, poll.id)) } case None => { val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND))) - sender ! new StopPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId) + sender ! new StopPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId) } } } def handleStartPollRequest(msg: StartPollRequest) { - log.debug("Received StartPollRequest for pollId=[" + msg.pollId + "]") - createPoll(msg) + log.debug("Received StartPollRequest for pollType=[" + msg.pollType + "]") - pollModel.getSimplePoll(msg.pollId) match { - case Some(poll) => { - pollModel.startPoll(poll.id) - outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId, poll)) - } - case None => { - val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND))) - sender ! new StartPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId) - } - } - } + presModel.getCurrentPage() foreach { page => + val pollId = page.id + "/" + System.currentTimeMillis() - private def createPoll(msg: StartPollRequest) { - PollFactory.createPoll(msg.pollId, msg.pollType) match { - case Some(poll) => { - pollModel.addPoll(poll) - } - case None => { - val result = new RequestResult(StatusCodes.NOT_ACCEPTABLE, Some(Array(ErrorCodes.INVALID_DATA))) - sender ! new StartPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId) - } - } - } + PollFactory.createPoll(pollId, msg.pollType) foreach (poll => pollModel.addPoll(poll)) - /* - def handleCreatePollRequest(msg: CreatePollRequest) { - PollFactory.createPoll(msg.pollId, msg.pollType) match { - case Some(poll) => { - pollModel.addPoll(poll) - outGW.send(new PollCreatedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId, poll.toPollVO())) - } - case None => { - val result = new RequestResult(StatusCodes.NOT_ACCEPTABLE, Some(Array(ErrorCodes.INVALID_DATA))) - sender ! new CreatePollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId, msg.pollType) + pollModel.getSimplePoll(pollId) match { + case Some(poll) => { + pollModel.startPoll(poll.id) + outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, poll)) + } + case None => { + val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND))) + sender ! new StartPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, pollId) + } } } } -*/ private def handleRespondToPoll(poll: SimplePollResultOutVO, msg: RespondToPollRequest) { if (hasUser(msg.requesterId)) { @@ -137,7 +126,7 @@ trait PollApp { */ val questionId = 0 pollModel.respondToQuestion(poll.id, questionId, msg.answerId, responder) - users.getCurrentPresenter foreach { cp => + usersModel.getCurrentPresenter foreach { cp => pollModel.getSimplePollResult(poll.id) foreach { updatedPoll => outGW.send(new UserRespondedToPollMessage(mProps.meetingID, mProps.recorded, cp.userID, msg.pollId, updatedPoll)) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollModel.scala index 85ac2ad609..beaa79afd7 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollModel.scala @@ -9,11 +9,12 @@ class PollModel { private var currentPoll: Option[PollVO] = None - /** - * * - * Uncomment to create sample polls for manual testing purposes - */ - //createSamplePoll + def getRunningPollThatStartsWith(pollId: String): Option[PollVO] = { + for { + poll <- polls.values find { poll => poll.id.startsWith(pollId) && poll.isRunning() } + } yield poll.toPollVO() + + } def numPolls(): Int = { polls.size @@ -125,64 +126,4 @@ class PollModel { } } - /** - * ****************************************************** - * Some pre-created polls for testing and simulation so we don't have - * to manually generate polls when testing the UI. - */ - /* - def createSamplePoll() { - addSamplePoll1() - addSamplePoll2() - addSamplePoll3() - } - - def addSamplePoll1() { - val r1 = new ResponseVO("0", "Visa") - val r2 = new ResponseVO("1", "MasterCard") - val r3 = new ResponseVO("2", "AmEx") - val r4 = new ResponseVO("3", "Diners Club") - - var q = new QuestionVO("q1", false, "What type of credit card do you prefer?", Array(r1, r2, r3, r4)) - val pvo = new PollVO("pollID-101", "Preferred Credit Card", Array(q)) - - createPoll(pvo) - - respondToQuestion("pollID-101", "q1", "1", new Responder("user1", "Juan Tamad")) - respondToQuestion("pollID-101", "q1", "0", new Responder("user2", "Asyong Aksaya")) - } - - def addSamplePoll2() { - val r1 = new ResponseVO("0", "Visa") - val r2 = new ResponseVO("1", "MasterCard") - val r3 = new ResponseVO("2", "AmEx") - val r4 = new ResponseVO("3", "Diners Club") - - var q = new QuestionVO("q1", true, "Which credit cards do you own?", Array(r1, r2, r3, r4)) - val pvo = new PollVO("pollID-102", "Owned Credit Cards", Array(q)) - - createPoll(pvo) - - respondToQuestion("pollID-102", "q1", "1", new Responder("user1", "Juan Tamad")) - respondToQuestion("pollID-102", "q1", "0", new Responder("user2", "Asyong Aksaya")) - } - - def addSamplePoll3() { - val r1 = new ResponseVO("0", "Dumaguete") - val r2 = new ResponseVO("1", "Cebu") - val r3 = new ResponseVO("2", "Zamboanga") - val r4 = new ResponseVO("3", "None of the above") - - var q = new QuestionVO("q1", true, "What is the capital of the Philippines?", Array(r1, r2, r3, r4)) - val pvo = new PollVO("pollID-103", "Philippine Capital", Array(q)) - - createPoll(pvo) - - respondToQuestion("pollID-103", "q1", "1", new Responder("user1", "Juan Tamad")) - respondToQuestion("pollID-103", "q1", "0", new Responder("user2", "Asyong")) - respondToQuestion("pollID-103", "q1", "2", new Responder("user3", "Pedro")) - respondToQuestion("pollID-103", "q1", "3", new Responder("user4", "Aksaya")) - } - */ - } \ No newline at end of file diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationApp.scala index 98a01949b5..baf6f64d62 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationApp.scala @@ -4,19 +4,12 @@ import org.bigbluebutton.core.api._ import org.bigbluebutton.core.MeetingActor import com.google.gson.Gson -case class CurrentPresenter(userId: String, name: String, assignedBy: String) - -case class CurrentPresentationInfo(presenter: CurrentPresenter, - presentations: Seq[Presentation]) -case class CursorLocation(xPercent: Double = 0D, yPercent: Double = 0D) - trait PresentationApp { this: MeetingActor => val outGW: MessageOutGateway private var cursorLocation = new CursorLocation - private val presModel = new PresentationModel def handlePreuploadedPresentations(msg: PreuploadedPresentations) { val pres = msg.presentations @@ -83,7 +76,7 @@ trait PresentationApp { def handleGetPresentationInfo(msg: GetPresentationInfo) { // println("PresentationApp : handleGetPresentationInfo GetPresentationInfo for meeting [" + msg.meetingID + "] [" + msg.requesterID + "]" ) - val curPresenter = users.getCurrentPresenterInfo(); + val curPresenter = usersModel.getCurrentPresenterInfo(); val presenter = new CurrentPresenter(curPresenter.presenterID, curPresenter.presenterName, curPresenter.assignedBy) val presentations = presModel.getPresentations val presentationInfo = new CurrentPresentationInfo(presenter, presentations) @@ -108,9 +101,15 @@ trait PresentationApp { presModel.changePage(msg.page) foreach { page => // println("Switching page for meeting=[" + msg.meetingID + "] page=[" + page.id + "]") outGW.send(new GotoSlideOutMsg(mProps.meetingID, mProps.recorded, page)) + } // println("*** After change page ****") // printPresentations + + usersModel.getCurrentPresenter() foreach { pres => + this.context.self ! StopPollRequest(mProps.meetingID, pres.userID) + } + } def handleSharePresentation(msg: SharePresentation) { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationModel.scala index 4f2a4318ae..47bfb994b3 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PresentationModel.scala @@ -1,5 +1,8 @@ package org.bigbluebutton.core.apps +case class CurrentPresenter(userId: String, name: String, assignedBy: String) +case class CurrentPresentationInfo(presenter: CurrentPresenter, presentations: Seq[Presentation]) +case class CursorLocation(xPercent: Double = 0D, yPercent: Double = 0D) case class Presentation(id: String, name: String, current: Boolean = false, pages: scala.collection.immutable.HashMap[String, Page]) @@ -31,6 +34,13 @@ class PresentationModel { pres.pages.values find (p => p.current) } + def getCurrentPage(): Option[Page] = { + for { + curPres <- getCurrentPresentation() + curPage <- getCurrentPage(curPres) + } yield curPage + } + def remove(presId: String): Option[Presentation] = { val pres = presentations.get(presId) pres foreach (p => presentations -= p.id) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala index a2efe5917e..cdf6cdb284 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala @@ -13,29 +13,29 @@ trait UsersApp { val outGW: MessageOutGateway def hasUser(userID: String): Boolean = { - users.hasUser(userID) + usersModel.hasUser(userID) } def getUser(userID: String): Option[UserVO] = { - users.getUser(userID) + usersModel.getUser(userID) } def handleUserConnectedToGlobalAudio(msg: UserConnectedToGlobalAudio) { - val user = users.getUserWithExternalId(msg.userid) + val user = usersModel.getUserWithExternalId(msg.userid) user foreach { u => val vu = u.voiceUser.copy(talking = false) val uvo = u.copy(listenOnly = true, voiceUser = vu) - users.addUser(uvo) + usersModel.addUser(uvo) log.info("UserConnectedToGlobalAudio: mid=[" + mProps.meetingID + "] uid=[" + uvo.userID + "]") outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.userID, uvo.listenOnly)) } } def handleUserDisconnectedFromGlobalAudio(msg: UserDisconnectedFromGlobalAudio) { - val user = users.getUserWithExternalId(msg.userid) + val user = usersModel.getUserWithExternalId(msg.userid) user foreach { u => val uvo = u.copy(listenOnly = false) - users.addUser(uvo) + usersModel.addUser(uvo) log.info("UserDisconnectedToGlobalAudio: mid=[" + mProps.meetingID + "] uid=[" + uvo.userID + "]") outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.userID, uvo.listenOnly)) } @@ -61,14 +61,14 @@ trait UsersApp { meetingModel.unmuteMeeting() } outGW.send(new MeetingMuted(mProps.meetingID, mProps.recorded, meetingModel.isMeetingMuted())) - users.getUsers foreach { u => + usersModel.getUsers foreach { u => outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded, msg.requesterID, u.userID, mProps.voiceBridge, u.voiceUser.userId, msg.mute)) } } def handleValidateAuthToken(msg: ValidateAuthToken) { // println("*************** Got ValidateAuthToken message ********************" ) - users.getRegisteredUserWithToken(msg.token) match { + usersModel.getRegisteredUserWithToken(msg.token) match { case Some(u) => { val replyTo = mProps.meetingID + '/' + msg.userId @@ -103,7 +103,7 @@ trait UsersApp { sendMeetingHasEnded(msg.userID) } else { val regUser = new RegisteredUser(msg.userID, msg.extUserID, msg.name, msg.role, msg.authToken) - users.addRegisteredUser(msg.authToken, regUser) + usersModel.addRegisteredUser(msg.authToken, regUser) log.info("Register user success: mid=[" + mProps.meetingID + "] uid=[" + msg.userID + "]") outGW.send(new UserRegistered(mProps.meetingID, mProps.recorded, regUser)) @@ -117,7 +117,7 @@ trait UsersApp { def handleMuteUserRequest(msg: MuteUserRequest) { // println("Received mute user request uid=[" + msg.userID + "] mute=[" + msg.mute + "]") - users.getUser(msg.userID) match { + usersModel.getUser(msg.userID) match { case Some(u) => { // println("Sending mute user request uid=[" + msg.userID + "] mute=[" + msg.mute + "]") log.info("Muting user: mid=[" + mProps.meetingID + "] uid=[" + u.userID + "]") @@ -132,7 +132,7 @@ trait UsersApp { def handleEjectUserRequest(msg: EjectUserFromVoiceRequest) { // println("Received eject user request uid=[" + msg.userID + "]") - users.getUser(msg.userId) match { + usersModel.getUser(msg.userId) match { case Some(u) => { if (u.voiceUser.joined) { log.info("Ejecting user from voice: mid=[" + mProps.meetingID + "] uid=[" + u.userID + "]") @@ -147,24 +147,24 @@ trait UsersApp { //println("*************** Reply with current lock settings ********************") //reusing the existing handle for NewPermissionsSettings to reply to the GetLockSettings request - outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.userId, meetingModel.getPermissions(), users.getUsers)) + outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.userId, meetingModel.getPermissions(), usersModel.getUsers)) } def handleSetLockSettings(msg: SetLockSettings) { // println("*************** Received new lock settings ********************") if (!permissionsEqual(msg.settings)) { newPermissions(msg.settings) - outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.setByUser, meetingModel.getPermissions, users.getUsers)) + outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.setByUser, meetingModel.getPermissions, usersModel.getUsers)) handleLockLayout(msg.settings.lockedLayout, msg.setByUser) } } def handleLockUserRequest(msg: LockUserRequest) { - users.getUser(msg.userID) match { + usersModel.getUser(msg.userID) match { case Some(u) => { val uvo = u.copy(locked = msg.lock) - users.addUser(uvo) + usersModel.addUser(uvo) log.info("Lock user: mid=[" + mProps.meetingID + "] uid=[" + u.userID + "] lock=[" + msg.lock + "]") outGW.send(new UserLocked(mProps.meetingID, u.userID, msg.lock)) @@ -179,7 +179,7 @@ trait UsersApp { if (!meetingModel.permisionsInitialized()) { meetingModel.initializePermissions() newPermissions(msg.settings) - outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.settings, users.getUsers)) + outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.settings, usersModel.getUsers)) } } @@ -196,7 +196,7 @@ trait UsersApp { def usersWhoAreNotPresenter(): Array[UserVO] = { val au = ArrayBuffer[UserVO]() - users.getUsers foreach { u => + usersModel.getUsers foreach { u => if (!u.presenter) { au += u } @@ -205,28 +205,28 @@ trait UsersApp { } def handleUserRaiseHand(msg: UserRaiseHand) { - users.getUser(msg.userId) foreach { user => + usersModel.getUser(msg.userId) foreach { user => val uvo = user.copy(raiseHand = true) - users.addUser(uvo) + usersModel.addUser(uvo) outGW.send(new UserRaisedHand(mProps.meetingID, mProps.recorded, uvo.raiseHand, uvo.userID)) } } def handleUserLowerHand(msg: UserLowerHand) { - users.getUser(msg.userId) foreach { user => + usersModel.getUser(msg.userId) foreach { user => val uvo = user.copy(raiseHand = false) - users.addUser(uvo) + usersModel.addUser(uvo) outGW.send(new UserLoweredHand(mProps.meetingID, mProps.recorded, uvo.raiseHand, uvo.userID, msg.loweredBy)) } } def handleEjectUserFromMeeting(msg: EjectUserFromMeeting) { - users.getUser(msg.userId) foreach { user => + usersModel.getUser(msg.userId) foreach { user => if (user.voiceUser.joined) { outGW.send(new EjectVoiceUser(mProps.meetingID, mProps.recorded, msg.ejectedBy, msg.userId, mProps.voiceBridge, user.voiceUser.userId)) } - users.removeUser(msg.userId) + usersModel.removeUser(msg.userId) log.info("Ejecting user from meeting: mid=[" + mProps.meetingID + "]uid=[" + msg.userId + "]") outGW.send(new UserEjectedFromMeeting(mProps.meetingID, mProps.recorded, msg.userId, msg.ejectedBy)) @@ -237,46 +237,45 @@ trait UsersApp { } def handleUserShareWebcam(msg: UserShareWebcam) { - users.getUser(msg.userId) foreach { user => + usersModel.getUser(msg.userId) foreach { user => val streams = user.webcamStreams + msg.stream val uvo = user.copy(hasStream = true, webcamStreams = streams) - users.addUser(uvo) + usersModel.addUser(uvo) log.info("User shared webcam: mid=[" + mProps.meetingID + "] uid=[" + uvo.userID + "] sharedStream=[" + msg.stream + "] streams=[" + streams + "]") outGW.send(new UserSharedWebcam(mProps.meetingID, mProps.recorded, uvo.userID, msg.stream)) } } def handleUserunshareWebcam(msg: UserUnshareWebcam) { - users.getUser(msg.userId) foreach { user => + usersModel.getUser(msg.userId) foreach { user => val streams = user.webcamStreams - msg.stream val uvo = user.copy(hasStream = (!streams.isEmpty), webcamStreams = streams) - users.addUser(uvo) + usersModel.addUser(uvo) log.info("User unshared webcam: mid=[" + mProps.meetingID + "] uid=[" + uvo.userID + "] unsharedStream=[" + msg.stream + "] streams=[" + streams + "]") outGW.send(new UserUnsharedWebcam(mProps.meetingID, mProps.recorded, uvo.userID, msg.stream)) } } def handleChangeUserStatus(msg: ChangeUserStatus): Unit = { - if (users.hasUser(msg.userID)) { + if (usersModel.hasUser(msg.userID)) { outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, msg.userID, msg.status, msg.value)) } } def handleGetUsers(msg: GetUsers): Unit = { - outGW.send(new GetUsersReply(msg.meetingID, msg.requesterID, users.getUsers)) + outGW.send(new GetUsersReply(msg.meetingID, msg.requesterID, usersModel.getUsers)) } def handleUserJoin(msg: UserJoining): Unit = { - val regUser = users.getRegisteredUserWithToken(msg.authToken) + val regUser = usersModel.getRegisteredUserWithToken(msg.authToken) regUser foreach { ru => - val vu = new VoiceUser(msg.userID, msg.userID, ru.name, ru.name, - false, false, false, false) + val vu = new VoiceUser(msg.userID, msg.userID, ru.name, ru.name, false, false, false, false) val uvo = new UserVO(msg.userID, ru.externId, ru.name, ru.role, raiseHand = false, presenter = false, hasStream = false, locked = getInitialLockStatus(ru.role), webcamStreams = new ListSet[String](), phoneUser = false, vu, listenOnly = false) - users.addUser(uvo) + usersModel.addUser(uvo) log.info("User joined meeting: mid=[" + mProps.meetingID + "] uid=[" + uvo.userID + "] role=[" + uvo.role + "] locked=[" + uvo.locked + "] permissions.lockOnJoin=[" + meetingModel.getPermissions().lockOnJoin @@ -286,7 +285,7 @@ trait UsersApp { outGW.send(new MeetingState(mProps.meetingID, mProps.recorded, uvo.userID, meetingModel.getPermissions(), meetingModel.isMeetingMuted())) // Become presenter if the only moderator - if (users.numModerators == 1) { + if (usersModel.numModerators == 1) { if (ru.role == Role.MODERATOR) { assignNewPresenter(msg.userID, ru.name, msg.userID) } @@ -297,22 +296,26 @@ trait UsersApp { } def handleUserLeft(msg: UserLeaving): Unit = { - if (users.hasUser(msg.userID)) { - val user = users.removeUser(msg.userID) + if (usersModel.hasUser(msg.userID)) { + val user = usersModel.removeUser(msg.userID) user foreach { u => log.info("User left meeting: mid=[" + mProps.meetingID + "] uid=[" + u.userID + "]") outGW.send(new UserLeft(msg.meetingID, mProps.recorded, u)) if (u.presenter) { + // Stop poll if one is running as presenter left. + this.context.self ! StopPollRequest(mProps.meetingID, u.userID) + /* The current presenter has left the meeting. Find a moderator and make * him presenter. This way, if there is a moderator in the meeting, there * will always be a presenter. */ - val moderator = users.findAModerator() + val moderator = usersModel.findAModerator() moderator.foreach { mod => log.info("Presenter left meeting: mid=[" + mProps.meetingID + "] uid=[" + u.userID + "]. Making user=[" + mod.userID + "] presenter.") assignNewPresenter(mod.userID, mod.name, mod.userID) } + } } @@ -326,7 +329,7 @@ trait UsersApp { } def handleUserJoinedVoiceFromPhone(msg: UserJoinedVoiceConfMessage) = { - val user = users.getUserWithVoiceUserId(msg.voiceUserId) match { + val user = usersModel.getUserWithVoiceUserId(msg.voiceUserId) match { case Some(user) => { log.info("Voice user=[" + msg.voiceUserId + "] is already in conf=[" + mProps.voiceBridge + "]. Must be duplicate message.") } @@ -334,7 +337,7 @@ trait UsersApp { // No current web user. This means that the user called in through // the phone. We need to generate a new user as we are not able // to match with a web user. - val webUserId = users.generateWebUserId + val webUserId = usersModel.generateWebUserId val vu = new VoiceUser(msg.voiceUserId, webUserId, msg.callerIdName, msg.callerIdNum, true, false, false, false) @@ -345,7 +348,7 @@ trait UsersApp { hasStream = false, locked = getInitialLockStatus(Role.VIEWER), webcamStreams = new ListSet[String](), phoneUser = true, vu, listenOnly = false) - users.addUser(uvo) + usersModel.addUser(uvo) log.info("New user joined voice for user [" + uvo.name + "] userid=[" + webUserId + "]") outGW.send(new UserJoined(mProps.meetingID, mProps.recorded, uvo)) @@ -358,7 +361,7 @@ trait UsersApp { } def startRecordingVoiceConference() { - if (users.numUsersInVoiceConference == 1 && mProps.recorded) { + if (usersModel.numUsersInVoiceConference == 1 && mProps.recorded) { log.info("********** Send START RECORDING [" + mProps.voiceBridge + "]") outGW.send(new StartRecordingVoiceConf(mProps.meetingID, mProps.recorded, mProps.voiceBridge)) } @@ -367,11 +370,11 @@ trait UsersApp { def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) = { log.info("Received user joined voice for user [" + msg.callerIdName + "] userid=[" + msg.userId + "]") - users.getUserWithExternalId(msg.userId) match { + usersModel.getUserWithExternalId(msg.userId) match { case Some(user) => { val vu = new VoiceUser(msg.voiceUserId, msg.userId, msg.callerIdName, msg.callerIdNum, true, false, msg.muted, msg.talking) val nu = user.copy(voiceUser = vu) - users.addUser(nu) + usersModel.addUser(nu) log.info("User joined voice for user [" + nu.name + "] userid=[" + msg.userId + "]") outGW.send(new UserJoinedVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu)) @@ -389,25 +392,25 @@ trait UsersApp { } def stopRecordingVoiceConference() { - if (users.numUsersInVoiceConference == 0 && mProps.recorded) { + if (usersModel.numUsersInVoiceConference == 0 && mProps.recorded) { log.info("********** Send STOP RECORDING [" + mProps.voiceBridge + "]") outGW.send(new StopRecordingVoiceConf(mProps.meetingID, mProps.recorded, mProps.voiceBridge, meetingModel.getVoiceRecordingFilename())) } } def handleUserLeftVoiceConfMessage(msg: UserLeftVoiceConfMessage) { - users.getUserWithVoiceUserId(msg.voiceUserId) foreach { user => + usersModel.getUserWithVoiceUserId(msg.voiceUserId) foreach { user => val vu = new VoiceUser(user.userID, user.userID, user.name, user.name, false, false, false, false) val nu = user.copy(voiceUser = vu) - users.addUser(nu) + usersModel.addUser(nu) // println("Received voice user left =[" + user.name + "] wid=[" + msg.userId + "]" ) log.info("Received user left voice for user [" + nu.name + "] userid=[" + msg.voiceUserId + "]") outGW.send(new UserLeftVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu)) if (user.phoneUser) { - if (users.hasUser(user.userID)) { - val userLeaving = users.removeUser(user.userID) + if (usersModel.hasUser(user.userID)) { + val userLeaving = usersModel.removeUser(user.userID) userLeaving foreach (u => outGW.send(new UserLeft(mProps.meetingID, mProps.recorded, u))) } } @@ -417,21 +420,21 @@ trait UsersApp { } def handleUserMutedInVoiceConfMessage(msg: UserMutedInVoiceConfMessage) { - users.getUserWithVoiceUserId(msg.voiceUserId) foreach { user => + usersModel.getUserWithVoiceUserId(msg.voiceUserId) foreach { user => val talking: Boolean = if (msg.muted) false else user.voiceUser.talking val nv = user.voiceUser.copy(muted = msg.muted, talking = talking) val nu = user.copy(voiceUser = nv) - users.addUser(nu) + usersModel.addUser(nu) // println("Received voice muted=[" + msg.muted + "] wid=[" + msg.userId + "]" ) outGW.send(new UserVoiceMuted(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu)) } } def handleUserTalkingInVoiceConfMessage(msg: UserTalkingInVoiceConfMessage) { - users.getUserWithVoiceUserId(msg.voiceUserId) foreach { user => + usersModel.getUserWithVoiceUserId(msg.voiceUserId) foreach { user => val nv = user.voiceUser.copy(talking = msg.talking) val nu = user.copy(voiceUser = nv) - users.addUser(nu) + usersModel.addUser(nu) // println("Received voice talking=[" + msg.talking + "] wid=[" + msg.userId + "]" ) outGW.send(new UserVoiceTalking(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu)) } @@ -442,20 +445,20 @@ trait UsersApp { } def assignNewPresenter(newPresenterID: String, newPresenterName: String, assignedBy: String) { - if (users.hasUser(newPresenterID)) { + if (usersModel.hasUser(newPresenterID)) { - users.getCurrentPresenter match { + usersModel.getCurrentPresenter match { case Some(curPres) => { - users.unbecomePresenter(curPres.userID) + usersModel.unbecomePresenter(curPres.userID) outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, curPres.userID, "presenter", false: java.lang.Boolean)) } case None => // do nothing } - users.getUser(newPresenterID) match { + usersModel.getUser(newPresenterID) match { case Some(newPres) => { - users.becomePresenter(newPres.userID) - users.setCurrentPresenterInfo(new Presenter(newPresenterID, newPresenterName, assignedBy)) + usersModel.becomePresenter(newPres.userID) + usersModel.setCurrentPresenterInfo(new Presenter(newPresenterID, newPresenterName, assignedBy)) outGW.send(new PresenterAssigned(mProps.meetingID, mProps.recorded, new Presenter(newPresenterID, newPresenterName, assignedBy))) outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, newPresenterID, "presenter", true: java.lang.Boolean)) }