Merge branch 'riadvice-recording-timer' into v2.0.x-release

This commit is contained in:
Richard Alam 2018-05-23 10:33:17 -07:00
commit 126bb62d7d
28 changed files with 491 additions and 196 deletions

View File

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

View File

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

View File

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

View File

@ -1,37 +1,56 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
trait SetRecordingStatusCmdMsgHdlr {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg) {
log.info("Change recording status. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording)
if (liveMeeting.props.recordProp.allowStartStopRecording &&
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
if (msg.body.recording) {
MeetingStatus2x.recordingStarted(liveMeeting.status)
} else {
MeetingStatus2x.recordingStopped(liveMeeting.status)
}
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
outGW.send(event)
}
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}
}
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
trait SetRecordingStatusCmdMsgHdlr {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg, state: MeetingState2x): MeetingState2x = {
log.info("Change recording status. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording)
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
if (liveMeeting.props.recordProp.allowStartStopRecording &&
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
if (msg.body.recording) {
MeetingStatus2x.recordingStarted(liveMeeting.status)
} else {
MeetingStatus2x.recordingStopped(liveMeeting.status)
}
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
outGW.send(event)
var newState = state
if (MeetingStatus2x.isRecording(liveMeeting.status)) {
val tracker = state.recordingTracker.startTimer(TimeUtil.timeNowInMs())
newState = state.update(tracker)
} else {
val tracker = state.recordingTracker.pauseTimer(TimeUtil.timeNowInMs())
newState = state.update(tracker)
}
eventBus.publish(BigBlueButtonEvent(liveMeeting.props.meetingProp.intId, SendRecordingTimerInternalMsg(liveMeeting.props.meetingProp.intId)))
newState
} else {
state
}
}
}

View File

@ -1,11 +1,10 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x }
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserLeaveReqMsgHdlr {
@ -20,7 +19,7 @@ trait UserLeaveReqMsgHdlr {
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u)
captionApp2x.handleUserLeavingMsg(msg.body.userId)
stopAutoStartedRecording()
stopRecordingIfAutoStart2x(outGW, liveMeeting, state)
// send a user left event for the clients to update
val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId)

View File

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

View File

@ -1,7 +1,5 @@
package org.bigbluebutton.core.domain
import org.bigbluebutton.core.util.TimeUtil
case class MeetingInactivityTracker(
val maxInactivityTimeoutInMs: Long,
val warningBeforeMaxInMs: Long,
@ -93,3 +91,31 @@ case class MeetingExpiryTracker(
}
}
case class MeetingRecordingTracker(
startedOnInMs: Long,
previousDurationInMs: Long,
currentDurationInMs: Long
) {
def startTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(startedOnInMs = nowInMs)
}
def pauseTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(currentDurationInMs = 0L, previousDurationInMs = previousDurationInMs + nowInMs - startedOnInMs, startedOnInMs = 0L)
}
def resetTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(startedOnInMs = nowInMs, previousDurationInMs = 0L, currentDurationInMs = 0L)
}
def udpateCurrentDuration(nowInMs: Long): MeetingRecordingTracker = {
copy(currentDurationInMs = nowInMs - startedOnInMs)
}
def recordingDuration(): Long = {
currentDurationInMs + previousDurationInMs
}
}

View File

@ -9,12 +9,14 @@ object MeetingState2x {
case class MeetingState2x(
breakout: Option[BreakoutModel],
inactivityTracker: MeetingInactivityTracker,
expiryTracker: MeetingExpiryTracker
expiryTracker: MeetingExpiryTracker,
recordingTracker: MeetingRecordingTracker
) {
def update(breakout: Option[BreakoutModel]) = copy(breakout = breakout)
def update(expiry: MeetingExpiryTracker): MeetingState2x = copy(expiryTracker = expiry)
def update(inactivityTracker: MeetingInactivityTracker): MeetingState2x = copy(inactivityTracker = inactivityTracker)
def update(recordingTracker: MeetingRecordingTracker): MeetingState2x = copy(recordingTracker = recordingTracker)
}
object MeetingEndReason {

View File

@ -238,6 +238,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[LogoutAndEndMeetingCmdMsg](envelope, jsonNode)
case SetRecordingStatusCmdMsg.NAME =>
routeGenericMsg[SetRecordingStatusCmdMsg](envelope, jsonNode)
case RecordAndClearPreviousMarkersCmdMsg.NAME =>
routeGenericMsg[RecordAndClearPreviousMarkersCmdMsg](envelope, jsonNode)
case GetRecordingStatusReqMsg.NAME =>
routeGenericMsg[GetRecordingStatusReqMsg](envelope, jsonNode)
case GetScreenshareStatusReqMsg.NAME =>

View File

@ -8,6 +8,7 @@ import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models._
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, UserJoinedMeetingEvtMsgBuilder }
import org.bigbluebutton.core.util.TimeUtil
trait HandlerHelpers extends SystemConfiguration {
@ -50,22 +51,25 @@ trait HandlerHelpers extends SystemConfiguration {
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser)
outGW.send(event)
startRecordingIfAutoStart2x(outGW, liveMeeting)
val newState = startRecordingIfAutoStart2x(outGW, liveMeeting, state)
if (!Users2x.hasPresenter(liveMeeting.users2x)) {
automaticallyAssignPresenter(outGW, liveMeeting)
}
state.update(state.expiryTracker.setUserHasJoined())
newState.update(newState.expiryTracker.setUserHasJoined())
case None =>
state
}
}
def startRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting): Unit = {
def startRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
var newState = state
if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
MeetingStatus2x.recordingStarted(liveMeeting.status)
val tracker = state.recordingTracker.startTimer(TimeUtil.timeNowInMs())
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
@ -81,8 +85,38 @@ trait HandlerHelpers extends SystemConfiguration {
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
newState = state.update(tracker)
}
newState
}
def stopRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
var newState = state
if (liveMeeting.props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) {
MeetingStatus2x.recordingStopped(liveMeeting.status)
val tracker = state.recordingTracker.pauseTimer(TimeUtil.timeNowInMs())
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
newState = state.update(tracker)
}
newState
}
def automaticallyAssignPresenter(outGW: OutMsgRouter, liveMeeting: LiveMeeting): Unit = {

View File

@ -4,9 +4,8 @@ import java.io.{ PrintWriter, StringWriter }
import org.bigbluebutton.core.apps.users._
import org.bigbluebutton.core.apps.whiteboard.ClientToServerLatencyTracerMsgHdlr
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x }
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingRecordingTracker, MeetingState2x }
import org.bigbluebutton.core.util.TimeUtil
//import java.util.concurrent.TimeUnit
import akka.actor._
import akka.actor.SupervisorStrategy.Resume
@ -127,7 +126,9 @@ class MeetingActor(
meetingExpireWhenLastUserLeftInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireWhenLastUserLeftInMinutes)
)
var state = new MeetingState2x(None, inactivityTracker, expiryTracker)
val recordingTracker = new MeetingRecordingTracker(startedOnInMs = 0L, previousDurationInMs = 0L, currentDurationInMs = 0L)
var state = new MeetingState2x(None, inactivityTracker, expiryTracker, recordingTracker)
var lastRttTestSentOn = System.currentTimeMillis()
@ -182,7 +183,10 @@ class MeetingActor(
// Screenshare
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
case _ => // do nothing
case msg: SendRecordingTimerInternalMsg =>
state = usersApp.handleSendRecordingTimerInternalMsg(msg, state)
case _ => // do nothing
}
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
@ -206,8 +210,11 @@ class MeetingActor(
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
case m: MeetingActivityResponseCmdMsg =>
state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m)
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
case m: SetRecordingStatusCmdMsg =>
state = usersApp.handleSetRecordingStatusCmdMsg(m, state)
case m: RecordAndClearPreviousMarkersCmdMsg =>
state = usersApp.handleRecordAndClearPreviousMarkersCmdMsg(m, state)
case m: GetWebcamsOnlyForModeratorReqMsg => usersApp.handleGetWebcamsOnlyForModeratorReqMsg(m)
case m: UpdateWebcamsOnlyForModeratorCmdMsg => usersApp.handleUpdateWebcamsOnlyForModeratorCmdMsg(m)
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
@ -401,53 +408,4 @@ class MeetingActor(
}
def startRecordingIfAutoStart() {
if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
MeetingStatus2x.recordingStarted(liveMeeting.status)
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
}
}
def stopAutoStartedRecording() {
if (props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) {
log.info("Last web user left. Auto stopping recording. meetingId={}", props.meetingProp.intId)
MeetingStatus2x.recordingStopped(liveMeeting.status)
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
}
}
}

View File

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

View File

@ -74,6 +74,8 @@ class AnalyticsActor extends Actor with ActorLogging {
case m: MeetingInactivityWarningEvtMsg => logMessage(msg)
case m: StartRecordingVoiceConfSysMsg => logMessage(msg)
case m: StopRecordingVoiceConfSysMsg => logMessage(msg)
case m: UpdateRecordingTimerEvtMsg => logMessage(msg)
case m: RecordAndClearPreviousMarkersCmdMsg => logMessage(msg)
case m: TransferUserToVoiceConfSysMsg => logMessage(msg)
case m: UserBroadcastCamStartMsg => logMessage(msg)
case m: UserBroadcastCamStopMsg => logMessage(msg)

View File

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

View File

@ -202,7 +202,6 @@ object MsgBuilder {
val header = BbbClientMsgHeader(UserLeftMeetingEvtMsg.NAME, meetingId, userId)
val body = UserLeftMeetingEvtMsgBody(userId)
val event = UserLeftMeetingEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
BbbCommonEnvCoreMsg(envelope, event)
}

View File

@ -92,6 +92,14 @@ object SetRecordingStatusCmdMsg { val NAME = "SetRecordingStatusCmdMsg" }
case class SetRecordingStatusCmdMsg(header: BbbClientMsgHeader, body: SetRecordingStatusCmdMsgBody) extends StandardMsg
case class SetRecordingStatusCmdMsgBody(recording: Boolean, setBy: String)
/**
* Sent by user to start recording mark and ignore previsous marks
*/
object RecordAndClearPreviousMarkersCmdMsg { val NAME = "RecordAndClearPreviousMarkersCmdMsg" }
case class RecordAndClearPreviousMarkersCmdMsg(header: BbbClientMsgHeader, body: RecordAndClearPreviousMarkersCmdMsgBody) extends StandardMsg
case class RecordAndClearPreviousMarkersCmdMsgBody(recording: Boolean, setBy: String)
/**
* Sent to all users about start recording mark.
*/
@ -99,6 +107,10 @@ object RecordingStatusChangedEvtMsg { val NAME = "RecordingStatusChangedEvtMsg"
case class RecordingStatusChangedEvtMsg(header: BbbClientMsgHeader, body: RecordingStatusChangedEvtMsgBody) extends BbbCoreMsg
case class RecordingStatusChangedEvtMsgBody(recording: Boolean, setBy: String)
object UpdateRecordingTimerEvtMsg {val NAME = "UpdateRecordingTimerEvtMsg"}
case class UpdateRecordingTimerEvtMsg(header: BbbClientMsgHeader, body: UpdateRecordingTimerEvtMsgBody) extends BbbCoreMsg
case class UpdateRecordingTimerEvtMsgBody(time: Long)
/**
* Sent by user to update webcamsOnlyForModerator meeting property.
*/

View File

@ -114,9 +114,6 @@ bbb.mainToolbar.recordBtn.toolTip.notRecording = The session isn't being recorde
bbb.mainToolbar.recordBtn.toolTip.onlyModerators = Only moderators can start and stop recordings
bbb.mainToolbar.recordBtn.toolTip.wontInterrupt = This recording can't be interrupted
bbb.mainToolbar.recordBtn.toolTip.wontRecord = This session cannot be recorded
bbb.mainToolbar.recordBtn.confirm.title = Confirm recording
bbb.mainToolbar.recordBtn.confirm.message.start = Are you sure you want to start recording the session?
bbb.mainToolbar.recordBtn.confirm.message.stop = Are you sure you want to stop recording the session?
bbb.mainToolbar.recordBtn.notification.title = Record Notification
bbb.mainToolbar.recordBtn.notification.message1 = You can record this meeting.
bbb.mainToolbar.recordBtn.notification.message2 = You must click the Start/Stop Recording button in the title bar to begin/end recording.
@ -124,6 +121,11 @@ bbb.mainToolbar.recordingLabel.recording = (Recording)
bbb.mainToolbar.recordingLabel.notRecording = Not Recording
bbb.waitWindow.waitMessage.message = You are a guest, please wait moderator approval.
bbb.waitWindow.waitMessage.title = Waiting
bbb.recordWindow.title.start = Start recording
bbb.recordWindow.title.stop = Stop recording
bbb.recordWindow.confirm.yes = Yes
bbb.recordWindow.confirm.no = No
bbb.recordWindow.clearCheckbox.label = Clear all previous recording marks
bbb.guests.title = Guests
bbb.guests.message.singular = {0} user wants to join this meeting
bbb.guests.message.plural = {0} users want to join this meeting

View File

@ -21,17 +21,19 @@ package org.bigbluebutton.core {
import flash.events.TimerEvent;
import flash.utils.Dictionary;
import flash.utils.Timer;
import mx.controls.Alert;
import mx.controls.Label;
import mx.managers.PopUpManager;
import org.bigbluebutton.util.i18n.ResourceUtil;
import org.bigbluebutton.util.i18n.ResourceUtil;
public final class TimerUtil {
public static var timers:Dictionary = new Dictionary(true);
private static var timers:Dictionary = new Dictionary(true);
public static function setCountDownTimer(label:Label, seconds:int, showMinuteWarning:Boolean=false):void {
private static var times:Dictionary = new Dictionary(true);
public static function setCountDownTimer(label:Label, seconds:int, showMinuteWarning:Boolean = false):void {
var timer:Timer = getTimer(label.id, seconds);
var minuteWarningShown:Boolean = false;
var minuteAlert:Alert = null;
@ -58,6 +60,23 @@ package org.bigbluebutton.core {
timer.start();
}
public static function setTimer(label:Label, seconds:int, running:Boolean):void {
var timer:Timer = getTimer(label.id, seconds);
if (!timer.hasEventListener(TimerEvent.TIMER)) {
timer.addEventListener(TimerEvent.TIMER, function():void {
var elapsedSeconds:int = times[timer] + timer.currentCount;
var formattedTime:String = (Math.floor(elapsedSeconds / 60)) + ":" + (elapsedSeconds % 60 >= 10 ? "" : "0") + (elapsedSeconds % 60);
label.text = formattedTime;
});
}
times[timer] = seconds - timer.currentCount;
if (running) {
timer.start();
} else {
timer.stop();
}
}
public static function getTimer(name:String, defaultRepeatCount:Number):Timer {
if (timers[name] == undefined) {
timers[name] = new Timer(1000, defaultRepeatCount);

View File

@ -0,0 +1,15 @@
package org.bigbluebutton.core.events {
import flash.events.Event;
public class UpdateRecordingTimerEvent extends Event {
public static const UPDATE_TIMER:String = "UPDATE_TIMER";
public var time:int;
public function UpdateRecordingTimerEvent(time:int) {
super(UPDATE_TIMER, false, false);
this.time = time;
}
}
}

View File

@ -47,11 +47,14 @@ package org.bigbluebutton.main.events {
public static const MIC_SETTINGS_CLOSED:String = "MIC_SETTINGS_CLOSED";
public static const CAM_SETTINGS_CLOSED:String = "CAM_SETTINGS_CLOSED";
public static const JOIN_VOICE_FOCUS_HEAD:String = "JOIN_VOICE_FOCUS_HEAD";
public static const RECORD_AND_CLEAR_PREVIOUS_MARKERS:String = "RECORD_AND_CLEAR_PREVIOUS_MARKERS";
public static const CHANGE_RECORDING_STATUS:String = "CHANGE_RECORDING_STATUS";
public static const CHANGE_WEBCAMS_ONLY_FOR_MODERATOR:String = "CHANGE_WEBCAMS_ONLY_FOR_MODERATOR";
public static const SETTINGS_CONFIRMED:String = "BBB_SETTINGS_CONFIRMED";
public static const SETTINGS_CANCELLED:String = "BBB_SETTINGS_CANCELLED";
public static const OPEN_RECORD_WINDOW : String = "OPEN_RECORD_WINDOW";
public static const ACCEPT_ALL_WAITING_GUESTS:String = "BBB_ACCEPT_ALL_WAITING_GUESTS";
public static const DENY_ALL_WAITING_GUESTS:String = "BBB_DENY_ALL_WAITING_GUESTS";

View File

@ -192,6 +192,12 @@ package org.bigbluebutton.main.model.users
}
}
public function recordAndClearPreviousMarkers(e:BBBEvent):void {
if (this.isModerator() && !e.payload.remote) {
sender.recordAndClearPreviousMarkers(UsersUtil.getMyUserID(), e.payload.recording);
}
}
public function userLoggedIn(e:UsersConnectionEvent):void {
LOGGER.debug("In userLoggedIn - reconnecting and allowed to join");
if (reconnecting && ! LiveMeeting.inst().me.waitingForApproval) {

View File

@ -86,6 +86,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mate:Listener type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}" method="closeWaitWindow"/>
<mate:Listener type="{RoundTripLatencyReceivedEvent.ROUND_TRIP_LATENCY_RECEIVED}" method="handleRoundTripLatencyReceivedEvent"/>
<mate:Listener type="{ConferenceCreatedEvent.CONFERENCE_CREATED_EVENT}" method="handleConferenceCreatedEvent" />
<mate:Listener type="{BBBEvent.OPEN_RECORD_WINDOW}" method="handleOpenRecordWindowEvent" />
</fx:Declarations>
<fx:Script>
@ -99,8 +101,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
import flash.net.navigateToURL;
import flash.utils.setTimeout;
import flexlib.mdi.effects.effectsLib.MDIVistaEffects;
import mx.binding.utils.ChangeWatcher;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
@ -109,6 +109,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
import mx.core.UIComponent;
import mx.events.FlexEvent;
import flexlib.mdi.effects.effectsLib.MDIVistaEffects;
import org.as3commons.lang.StringUtils;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
@ -850,6 +852,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
}
}
private function handleOpenRecordWindowEvent(e:BBBEvent):void {
var popUp:RecordWindow = PopUpUtil.createModalPopUp(FlexGlobals.topLevelApplication as DisplayObject, RecordWindow, true) as RecordWindow;
popUp.setRecordingFlag(e.payload.recording);
}
private function onFooterLinkClicked(e:TextEvent):void{
if (ExternalInterface.available) {
ExternalInterface.call("chatLinkClicked", e.text);

View File

@ -47,10 +47,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mate:Listener type="{SettingsComponentEvent.ADD}" method="addSettingsComponent" />
<mate:Listener type="{SettingsComponentEvent.REMOVE}" method="removeSettingsComponent"/>
<mate:Listener type="{BBBEvent.CHANGE_RECORDING_STATUS}" method="onRecordingStatusChanged" />
<mate:Listener type="{BBBEvent.RECORD_AND_CLEAR_PREVIOUS_MARKERS}" method="onRecordAndClearPreviousMarkersChanged" />
<mate:Listener type="{SuccessfulLoginEvent.USER_LOGGED_IN}" method="refreshModeratorButtonsVisibility" />
<mate:Listener type="{ChangeMyRole.CHANGE_MY_ROLE_EVENT}" method="refreshRole" />
<mate:Listener type="{BBBEvent.CONFIRM_LOGOUT_END_MEETING_EVENT}" method="confirmEndSession" />
<mate:Listener type="{MeetingTimeRemainingEvent.TIME_REMAINING}" method="handleRemainingTimeUpdate" />
<mate:Listener type="{UpdateRecordingTimerEvent.UPDATE_TIMER}" method="handleUpdateRecordingTimer" />
</fx:Declarations>
<fx:Script>
<![CDATA[
@ -79,6 +81,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
import org.bigbluebutton.core.TimerUtil;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.MeetingTimeRemainingEvent;
import org.bigbluebutton.core.events.UpdateRecordingTimerEvent;
import org.bigbluebutton.core.model.Config;
import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.main.events.BBBEvent;
@ -196,6 +199,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
initBandwidthToolTip();
}
}
private function handleRemainingTimeUpdate(e:MeetingTimeRemainingEvent):void {
// Display timer only if there is less than 30 minutes remaining
if (!timeRemaining.visible && e.timeLeftInSec <= 1800) {
@ -203,6 +207,15 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
}
TimerUtil.setCountDownTimer(timeRemaining, e.timeLeftInSec, true);
}
private function handleUpdateRecordingTimer(e:UpdateRecordingTimerEvent):void {
if (e.time == 0) {
recordingTimer.visible = false;
} else {
recordingTimer.visible = true;
TimerUtil.setTimer(recordingTimer, e.time, LiveMeeting.inst().meetingStatus.isRecording);
}
}
private function retrieveMeetingName(e:ConferenceCreatedEvent):void {
if (toolbarOptions.showMeetingName) {
@ -476,21 +489,27 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
private function focusLogoutButton(e:ShortcutEvent):void{
btnLogout.setFocus();
}
private function onRecordingStatusChanged(event:BBBEvent):void {
meetingNameLbl.text = "";
if (event.payload.recording) {
meetingNameLbl.text = ResourceUtil.getInstance().getString('bbb.mainToolbar.recordingLabel.recording')
}
if (toolbarOptions.showMeetingName) {
var meetingTitle:String = LiveMeeting.inst().meeting.name;
if (meetingTitle != null) {
meetingNameLbl.text += " " + meetingTitle;
}
}
}
private function onRecordingStatusChanged(event:BBBEvent):void {
if (event.payload.recording) {
meetingNameLbl.text = ResourceUtil.getInstance().getString('bbb.mainToolbar.recordingLabel.recording')
} else {
meetingNameLbl.text = "";
TimerUtil.stopTimer(recordingTimer.id);
}
if (toolbarOptions.showMeetingName) {
var meetingTitle:String = LiveMeeting.inst().meeting.name;
if (meetingTitle != null) {
meetingNameLbl.text += " " + meetingTitle;
}
}
}
private function onRecordAndClearPreviousMarkersChanged(event:BBBEvent):void {
recordingTimer.visible = false;
}
private function onNetStatsButtonClick(e:Event = null):void {
var d:Dispatcher = new Dispatcher();
@ -582,6 +601,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mx:HBox id="addedBtnsDeskShare" />
</mx:HBox>
<mx:HBox id="rightBox" width="40%" height="100%" horizontalAlign="right" verticalAlign="middle" horizontalScrollPolicy="off">
<mx:Label id="recordingTimer" visible="false" includeInLayout="{recordingTimer.visible}"/>
<views:RecordButton id="recordBtn" visible="{showRecordButton}" includeInLayout="{showRecordButton}"/>
<views:WebRTCAudioStatus id="webRTCAudioStatus" height="30"/>

View File

@ -44,14 +44,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flash.net.navigateToURL;
import mx.controls.Alert;
import mx.events.CloseEvent;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.Options;
import org.bigbluebutton.core.PopUpUtil;
import org.bigbluebutton.core.UsersUtil;
@ -65,74 +61,19 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
private static const LOGGER:ILogger = getClassLogger(RecordButton);
private var recordingFlag:Boolean;
private var firstAudioJoin:Boolean = true;
private var layoutOptions:LayoutOptions = null;
private var _confirmationAlert:Alert = null;
private function onCreationComplete():void {
ResourceUtil.getInstance().addEventListener(Event.CHANGE, localeChanged); // Listen for locale changing
}
private function hideConfirmationAlert():void {
if (_confirmationAlert != null) {
PopUpUtil.unlockPosition(_confirmationAlert);
_confirmationAlert = null;
}
}
private function showConfirmationAlert():void {
hideConfirmationAlert();
var message:String = recordingFlag? ResourceUtil.getInstance().getString('bbb.mainToolbar.recordBtn.confirm.message.start'): ResourceUtil.getInstance().getString('bbb.mainToolbar.recordBtn.confirm.message.stop');
// Confirm logout using built-in alert
_confirmationAlert = Alert.show(message, ResourceUtil.getInstance().getString('bbb.mainToolbar.recordBtn.confirm.title'), Alert.YES | Alert.NO, this, onCloseConfirmationDialog, null, Alert.YES);
PopUpUtil.lockPosition(_confirmationAlert, function():Point {
return new Point(parentApplication.width - 150 - _confirmationAlert.width, y + height + 5);
});
}
private function confirmChangeRecordingStatus():void {
LOGGER.debug("Confirming recording status change!!!!");
// need to save the flag in case of any remote update on the recording status
recordingFlag = !this.selected;
showConfirmationAlert();
}
private function onCloseConfirmationDialog(e:CloseEvent):void {
// check to see if the YES button was pressed
if (e.detail==Alert.YES) {
doChangeRecordingStatus();
}
hideConfirmationAlert();
}
private function doChangeRecordingStatus():void {
if (recordingFlag == this.selected) {
// while the user was confirming the recording change, the
// button state changed to the desired one (another user started
// or stopped to record), so we do nothing
return;
}
var event:BBBEvent = new BBBEvent(BBBEvent.CHANGE_RECORDING_STATUS);
var event:BBBEvent = new BBBEvent(BBBEvent.OPEN_RECORD_WINDOW);
event.payload.remote = false;
event.payload.recording = recordingFlag;
event.payload.recording = !LiveMeeting.inst().meetingStatus.isRecording;
var d:Dispatcher = new Dispatcher();
d.dispatchEvent(event);
this.enabled = false;
LOGGER.debug("RecordButton:doChangeRecordingStatus changing record status to {0}", [event.payload.recording]);
if (!recordingFlag && getLayoutOptions().logoutOnStopRecording) {
LOGGER.debug("Using 'logoutOnStopRecording' option to logout user after stopping recording");
navigateToURL(new URLRequest(BBB.getLogoutURL()), "_self");
}
}
private function updateButton(recording:Boolean):void {
@ -144,9 +85,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
this.enabled = UsersUtil.amIModerator()
&& LiveMeeting.inst().meeting.allowStartStopRecording;
}
if (! this.enabled) {
hideConfirmationAlert();
if (!this.enabled) {
PopUpUtil.removePopUp(RecordWindow);
}
}

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
implements="org.bigbluebutton.common.IKeyboardClose"
xmlns:common="org.bigbluebutton.common.*"
showCloseButton="false"
layout="absolute"
show="this.setFocus()">
<fx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flash.net.navigateToURL;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.Options;
import org.bigbluebutton.core.PopUpUtil;
import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.model.options.LayoutOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
private static const LOGGER:ILogger = getClassLogger(RecordWindow);
[Bindable]
private var _recordingFlag : Boolean;
private var layoutOptions:LayoutOptions = null;
public function setRecordingFlag(recording : Boolean):void {
_recordingFlag = recording;
}
private function getLayoutOptions() : LayoutOptions {
if (layoutOptions == null) {
layoutOptions = Options.getOptions(LayoutOptions) as LayoutOptions;
}
return layoutOptions;
}
protected function yesButton_clickHandler(e:MouseEvent):void {
if (_recordingFlag == LiveMeeting.inst().meetingStatus.isRecording) {
// while the user was confirming the recording change, the
// button state changed to the desired one (another user started
// or stopped to record), so we do nothing
close();
}
var event:BBBEvent;
if (_recordingFlag && clearRecordingsChecbox.selected) {
event = new BBBEvent(BBBEvent.RECORD_AND_CLEAR_PREVIOUS_MARKERS);
} else {
event = new BBBEvent(BBBEvent.CHANGE_RECORDING_STATUS);
}
event.payload.remote = false;
event.payload.recording = _recordingFlag;
var d:Dispatcher = new Dispatcher();
d.dispatchEvent(event);
LOGGER.debug("Changing record status to {0}", [event.payload.recording]);
if (!_recordingFlag && getLayoutOptions().logoutOnStopRecording) {
LOGGER.debug("Using 'logoutOnStopRecording' option to logout user after stopping recording");
navigateToURL(new URLRequest(BBB.getLogoutURL()), "_self");
}
close();
}
protected function close():void {
PopUpUtil.removePopUp(this);
}
]]>
</fx:Script>
<mx:VBox width="100%" height="100%" paddingBottom="25" paddingLeft="70" paddingRight="70" paddingTop="25"
horizontalAlign="center" verticalGap="16">
<common:AdvancedLabel maxWidth="550"
id="textArea"
text="{_recordingFlag ? ResourceUtil.getInstance().getString('bbb.recordWindow.title.start') : ResourceUtil.getInstance().getString('bbb.recordWindow.title.stop')}"
styleName="titleWindowStyle" />
<mx:HBox horizontalAlign="center" width="100%">
<mx:Button id="yesButton" styleName="mainActionButton" click="yesButton_clickHandler(event)" label="{ResourceUtil.getInstance().getString('bbb.recordWindow.confirm.yes')}" width="120"/>
<mx:Button id="noButton" styleName="mainActionButton" click="close()" label="{ResourceUtil.getInstance().getString('bbb.recordWindow.confirm.no')}" width="102"/>
</mx:HBox>
<mx:CheckBox id="clearRecordingsChecbox" label="{ResourceUtil.getInstance().getString('bbb.recordWindow.clearCheckbox.label')}"
visible="{_recordingFlag}" includeInLayout="{_recordingFlag}"/>
</mx:VBox>
<mx:Button id="closeButton" click="close()" styleName="titleWindowCloseButton"
toolTip="{ResourceUtil.getInstance().getString('bbb.micSettings.cancel')}"
right="10" top="15"
accessibilityName="{ResourceUtil.getInstance().getString('bbb.micSettings.cancel.toolTip')}" />
</mx:TitleWindow>

View File

@ -109,7 +109,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<EventHandlers type="{BBBEvent.CHANGE_RECORDING_STATUS}">
<MethodInvoker generator="{UserService}" method="changeRecordingStatus" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{BBBEvent.RECORD_AND_CLEAR_PREVIOUS_MARKERS}">
<MethodInvoker generator="{UserService}" method="recordAndClearPreviousMarkers" arguments="{event}" />
</EventHandlers>
<EventHandlers type="{BBBEvent.ACTIVITY_RESPONSE_EVENT}">
<MethodInvoker generator="{UserService}" method="activityResponse" />
</EventHandlers>

View File

@ -31,6 +31,7 @@ package org.bigbluebutton.modules.users.services
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.events.MeetingTimeRemainingEvent;
import org.bigbluebutton.core.events.NewGuestWaitingEvent;
import org.bigbluebutton.core.events.UpdateRecordingTimerEvent;
import org.bigbluebutton.core.events.UserEmojiChangedEvent;
import org.bigbluebutton.core.events.UserStatusChangedEvent;
import org.bigbluebutton.core.model.LiveMeeting;
@ -135,6 +136,9 @@ package org.bigbluebutton.modules.users.services
case "UserEmojiChangedEvtMsg":
handleEmojiStatusHand(message);
break;
case "UpdateRecordingTimerEvtMsg":
handleUpdateRecordingTimer(message);
break;
case "GetRecordingStatusRespMsg":
handleGetRecordingStatusReply(message);
break;
@ -710,6 +714,11 @@ package org.bigbluebutton.modules.users.services
}
}
private function handleUpdateRecordingTimer(msg:Object):void {
var e:UpdateRecordingTimerEvent = new UpdateRecordingTimerEvent(msg.body.time);
dispatcher.dispatchEvent(e);
}
private function sendUserEmojiChangedEvent(userId: String, emoji: String):void{
var dispatcher:Dispatcher = new Dispatcher();

View File

@ -329,6 +329,27 @@ package org.bigbluebutton.modules.users.services
); //_netConnection.call
}
public function recordAndClearPreviousMarkers(userID:String, recording:Boolean):void {
var message:Object = {
header: {name: "RecordAndClearPreviousMarkersCmdMsg", meetingId: UsersUtil.getInternalMeetingID(),
userId: UsersUtil.getMyUserID()},
body: {recording: recording, setBy: userID}
};
var _nc:ConnectionManager = BBB.initConnectionManager();
_nc.sendMessage2x(
function(result:String):void { // On successful result
},
function(status:String):void { // status - On error occurred
var logData:Object = UsersUtil.initLogData();
logData.tags = ["apps"];
logData.logCode = "error_sending_change_recording_status";
LOGGER.info(JSON.stringify(logData));
},
JSON.stringify(message)
); //_netConnection.call
}
public function muteAllUsers(mute:Boolean):void {
var message:Object = {
header: {name: "MuteMeetingCmdMsg", meetingId: UsersUtil.getInternalMeetingID(),