diff --git a/akka-bbb-apps/build.sbt b/akka-bbb-apps/build.sbt index 84bd7c904c9cc36da0a34d69b94e700df56fac17..5954a0627f8086ecec630d0775b2924b4d5eab2c 100755 --- a/akka-bbb-apps/build.sbt +++ b/akka-bbb-apps/build.sbt @@ -55,7 +55,7 @@ libraryDependencies ++= { "org.pegdown" % "pegdown" % "1.4.0", "junit" % "junit" % "4.11", "com.etaty.rediscala" %% "rediscala" % "1.4.0", - "commons-codec" % "commons-codec" % "1.8", + "commons-codec" % "commons-codec" % "1.10", "joda-time" % "joda-time" % "2.3", "com.google.code.gson" % "gson" % "2.5", "redis.clients" % "jedis" % "2.7.2", diff --git a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java index 1c586454e57a50eded00f495ef5337257e10b48f..cf1100af5bd4f1133033b3df8adfe11fcf800e0c 100755 --- a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java +++ b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java @@ -35,7 +35,7 @@ public class MeetingMessageReceiver implements MessageHandler { public void handleMessage(String pattern, String channel, String message) { if (channel.equalsIgnoreCase(MessagingConstants.TO_MEETING_CHANNEL)) { - System.out.println("Meeting message: " + channel + " " + message); + System.out.println("Meeting message: " + channel + " " + message); JsonParser parser = new JsonParser(); JsonObject obj = (JsonObject) parser.parse(message); @@ -45,8 +45,8 @@ public class MeetingMessageReceiver implements MessageHandler { String messageName = header.get("name").getAsString(); if (CreateMeetingRequest.NAME.equals(messageName)) { Gson gson = new Gson(); - CreateMeetingRequest msg = gson.fromJson(message, - CreateMeetingRequest.class); + CreateMeetingRequest msg = gson.fromJson(message, + CreateMeetingRequest.class); bbbGW.handleBigBlueButtonMessage(msg); } } @@ -59,49 +59,50 @@ public class MeetingMessageReceiver implements MessageHandler { EndMeetingMessage emm = (EndMeetingMessage) msg; bbbGW.endMeeting(emm.meetingId); } else if (msg instanceof RegisterUserMessage) { - RegisterUserMessage emm = (RegisterUserMessage) msg; - bbbGW.registerUser(emm.meetingID, emm.internalUserId, emm.fullname, emm.role, emm.externUserID, emm.authToken, emm.avatarURL); + RegisterUserMessage rum = (RegisterUserMessage) msg; + bbbGW.registerUser(rum.meetingID, rum.internalUserId, rum.fullname, rum.role, rum.externUserID, rum.authToken, rum.avatarURL); } else if (msg instanceof DestroyMeetingMessage) { - DestroyMeetingMessage emm = (DestroyMeetingMessage) msg; - bbbGW.destroyMeeting(emm.meetingId); + DestroyMeetingMessage dmm = (DestroyMeetingMessage) msg; + bbbGW.destroyMeeting(dmm.meetingId); } else if (msg instanceof ValidateAuthTokenMessage) { - ValidateAuthTokenMessage emm = (ValidateAuthTokenMessage) msg; + ValidateAuthTokenMessage vam = (ValidateAuthTokenMessage) msg; String sessionId = "tobeimplemented"; - bbbGW.validateAuthToken(emm.meetingId, emm.userId, emm.token, emm.replyTo, sessionId); + bbbGW.validateAuthToken(vam.meetingId, vam.userId, vam.token, vam.replyTo, sessionId); } else if (msg instanceof UserConnectedToGlobalAudio) { - UserConnectedToGlobalAudio emm = (UserConnectedToGlobalAudio) msg; + UserConnectedToGlobalAudio ustga = (UserConnectedToGlobalAudio) msg; Map<String, Object> logData = new HashMap<String, Object>(); - logData.put("voiceConf", emm.voiceConf); - logData.put("userId", emm.userid); - logData.put("username", emm.name); + logData.put("voiceConf", ustga.voiceConf); + logData.put("userId", ustga.userid); + logData.put("username", ustga.name); logData.put("event", "user_connected_to_global_audio"); logData.put("description", "User connected to global audio."); + /* Gson gson = new Gson(); String logStr = gson.toJson(logData); - - //System.out.println("User connected to global audio: data={}", logStr); - - bbbGW.userConnectedToGlobalAudio(emm.voiceConf, emm.userid, emm.name); + System.out.println("User connected to global audio: data={}", logStr); + */ + bbbGW.userConnectedToGlobalAudio(ustga.voiceConf, ustga.userid, ustga.name); } else if (msg instanceof UserDisconnectedFromGlobalAudio) { - UserDisconnectedFromGlobalAudio emm = (UserDisconnectedFromGlobalAudio) msg; + UserDisconnectedFromGlobalAudio udfga = (UserDisconnectedFromGlobalAudio) msg; Map<String, Object> logData = new HashMap<String, Object>(); - logData.put("voiceConf", emm.voiceConf); - logData.put("userId", emm.userid); - logData.put("username", emm.name); + logData.put("voiceConf", udfga.voiceConf); + logData.put("userId", udfga.userid); + logData.put("username", udfga.name); logData.put("event", "user_disconnected_from_global_audio"); logData.put("description", "User disconnected from global audio."); + /* Gson gson = new Gson(); String logStr = gson.toJson(logData); - - //System.out.println("User disconnected from global audio: data={}", logStr); - bbbGW.userDisconnectedFromGlobalAudio(emm.voiceConf, emm.userid, emm.name); + System.out.println("User disconnected from global audio: data={}", logStr); + */ + bbbGW.userDisconnectedFromGlobalAudio(udfga.voiceConf, udfga.userid, udfga.name); } else if (msg instanceof GetAllMeetingsRequest) { - GetAllMeetingsRequest emm = (GetAllMeetingsRequest) msg; + GetAllMeetingsRequest gamr = (GetAllMeetingsRequest) msg; bbbGW.getAllMeetings("no_need_of_a_meeting_id"); } else { System.out.println("Unknown message: [" + message + "]"); @@ -126,8 +127,8 @@ public class MeetingMessageReceiver implements MessageHandler { if (msg != null) { if (msg instanceof KeepAliveMessage) { - KeepAliveMessage emm = (KeepAliveMessage) msg; - bbbGW.isAliveAudit(emm.keepAliveId); + KeepAliveMessage kam = (KeepAliveMessage) msg; + bbbGW.isAliveAudit(kam.keepAliveId); } } else { System.out.println("Unknown message: [" + message + "]"); diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala index 9fd760f88e270994dae062df04fadfe1e7892dd2..a2e84486c09eb7b039caf2722f915b389a0bbadf 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala @@ -11,13 +11,13 @@ import org.bigbluebutton.SystemConfiguration object BigBlueButtonActor extends SystemConfiguration { def props(system: ActorSystem, - eventBus: IncomingEventBus, - outGW: OutMessageGateway): Props = + eventBus: IncomingEventBus, + outGW: OutMessageGateway): Props = Props(classOf[BigBlueButtonActor], system, eventBus, outGW) } class BigBlueButtonActor(val system: ActorSystem, - eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging { + eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging { implicit def executionContext = system.dispatcher implicit val timeout = Timeout(5 seconds) @@ -25,19 +25,19 @@ class BigBlueButtonActor(val system: ActorSystem, private var meetings = new collection.immutable.HashMap[String, RunningMeeting] def receive = { - case msg: CreateMeeting => handleCreateMeeting(msg) - case msg: DestroyMeeting => handleDestroyMeeting(msg) - case msg: KeepAliveMessage => handleKeepAliveMessage(msg) - case msg: PubSubPing => handlePubSubPingMessage(msg) - case msg: ValidateAuthToken => handleValidateAuthToken(msg) - case msg: GetAllMeetingsRequest => handleGetAllMeetingsRequest(msg) - case msg: UserJoinedVoiceConfMessage => handleUserJoinedVoiceConfMessage(msg) - case msg: UserLeftVoiceConfMessage => handleUserLeftVoiceConfMessage(msg) - case msg: UserLockedInVoiceConfMessage => handleUserLockedInVoiceConfMessage(msg) - case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg) - case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg) + case msg: CreateMeeting => handleCreateMeeting(msg) + case msg: DestroyMeeting => handleDestroyMeeting(msg) + case msg: KeepAliveMessage => handleKeepAliveMessage(msg) + case msg: PubSubPing => handlePubSubPingMessage(msg) + case msg: ValidateAuthToken => handleValidateAuthToken(msg) + case msg: GetAllMeetingsRequest => handleGetAllMeetingsRequest(msg) + case msg: UserJoinedVoiceConfMessage => handleUserJoinedVoiceConfMessage(msg) + case msg: UserLeftVoiceConfMessage => handleUserLeftVoiceConfMessage(msg) + case msg: UserLockedInVoiceConfMessage => handleUserLockedInVoiceConfMessage(msg) + case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg) + case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg) case msg: VoiceConfRecordingStartedMessage => handleVoiceConfRecordingStartedMessage(msg) - case _ => // do nothing + case _ => // do nothing } private def findMeetingWithVoiceConfId(voiceConfId: String): Option[RunningMeeting] = { @@ -117,9 +117,9 @@ class BigBlueButtonActor(val system: ActorSystem, meetings -= msg.meetingID log.info("Kick everyone out on meetingId={}", msg.meetingID) if (m.mProps.isBreakout) { - log.info("Informing parent meeting {} that a breakout room has been ended {}", m.mProps.externalMeetingID, m.mProps.meetingID) - eventBus.publish(BigBlueButtonEvent(m.mProps.externalMeetingID, - BreakoutRoomEnded(m.mProps.externalMeetingID, m.mProps.meetingID))) + log.info("Informing parent meeting {} that a breakout room has been ended {}", m.mProps.parentMeetingID, m.mProps.meetingID) + eventBus.publish(BigBlueButtonEvent(m.mProps.parentMeetingID, + BreakoutRoomEnded(m.mProps.parentMeetingID, m.mProps.meetingID))) } outGW.send(new EndAndKickAll(msg.meetingID, m.mProps.recorded)) outGW.send(new DisconnectAllUsers(msg.meetingID)) @@ -149,9 +149,9 @@ class BigBlueButtonActor(val system: ActorSystem, eventBus.subscribe(m.actorRef, m.mProps.deskshareBridge) meetings += m.mProps.meetingID -> m - outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.recorded, m.mProps.meetingName, - m.mProps.voiceBridge, msg.mProps.duration, msg.mProps.moderatorPass, - msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate)) + outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.parentMeetingID, + m.mProps.recorded, m.mProps.meetingName, m.mProps.voiceBridge, msg.mProps.duration, msg.mProps.moderatorPass, + msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate, msg.mProps.isBreakout)) m.actorRef ! new InitializeMeeting(m.mProps.meetingID, m.mProps.recorded) } 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 2b83e37d440b1daa3a078d0b97daeb1f6a74c383..7989bf5c84dc8ddb22197f6d6cc0852fade7ce81 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 @@ -57,6 +57,7 @@ class BigBlueButtonInGW( val mProps = new MeetingProperties( msg.payload.id, msg.payload.externalId, + msg.payload.parentId, msg.payload.name, msg.payload.record, msg.payload.voiceConfId, @@ -69,7 +70,8 @@ class BigBlueButtonInGW( msg.payload.createTime, msg.payload.createDate, red5DeskShareIP, red5DeskShareApp, - msg.payload.isBreakout) + msg.payload.isBreakout, + msg.payload.sequence) eventBus.publish(BigBlueButtonEvent("meeting-manager", new CreateMeeting(msg.payload.id, mProps))) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala index cc2118cd0099bb6ccf6e434b33e7eff8e2f51e98..67d50713dde15387588e3c386b761c4723d2dfb0 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala @@ -51,7 +51,7 @@ object JsonMessageDecoder { def decode(json: String): Option[InMessage] = { unmarshall(json) match { case Success(validMsg) => Some(validMsg) - case Failure(ex) => None + case Failure(ex) => None } } @@ -75,4 +75,4 @@ object JsonMessageDecoder { case _ => throw MessageProcessException("Cannot parse JSON message: [" + msg + "]") } } -} \ No newline at end of file +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala index 9ec02759c9a10fceeccab00f176d1e270e1cb18d..17d133ab66883beefa6d8e7a3a15cfa923619d8f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala @@ -38,27 +38,27 @@ class JsonMessageSenderActor(val service: MessageSender) def receive = { // Breakout - case msg: CreateBreakoutRoom => handleCreateBreakoutRoom(msg) - case msg: EndBreakoutRoom => handleEndBreakoutRoom(msg) - case msg: BreakoutRoomsListOutMessage => handleBreakoutRoomsList(msg) + case msg: CreateBreakoutRoom => handleCreateBreakoutRoom(msg) + case msg: EndBreakoutRoom => handleEndBreakoutRoom(msg) + case msg: BreakoutRoomsListOutMessage => handleBreakoutRoomsList(msg) case msg: BreakoutRoomJoinURLOutMessage => handleBreakoutRoomJoinURL(msg) case msg: BreakoutRoomStartedOutMessage => handleBreakoutRoomStarted(msg) - case msg: BreakoutRoomEndedOutMessage => handleBreakoutRoomEnded(msg) + case msg: BreakoutRoomEndedOutMessage => handleBreakoutRoomEnded(msg) case msg: UpdateBreakoutUsersOutMessage => handleUpdateBreakoutUsers(msg) - case msg: MeetingTimeRemainingUpdate => handleMeetingTimeRemainingUpdate(msg) + case msg: MeetingTimeRemainingUpdate => handleMeetingTimeRemainingUpdate(msg) - case _ => // do nothing + case _ => // do nothing } // Breakout private def handleBreakoutRoomStarted(msg: BreakoutRoomStartedOutMessage) { - val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakout.breakoutId, msg.breakout.name) + val payload = new BreakoutRoomPayload(msg.parentMeetingId, msg.breakout.meetingId, msg.breakout.externalMeetingId, msg.breakout.name, msg.breakout.sequence) val request = new BreakoutRoomStarted(payload) service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson) } private def handleBreakoutRoomEnded(msg: BreakoutRoomEndedOutMessage) { - val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakoutId, "") + val payload = new BreakoutRoomPayload(msg.parentMeetingId, msg.meetingId, "", "", 0) val request = new BreakoutRoomClosed(payload) service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson) } @@ -66,7 +66,7 @@ class JsonMessageSenderActor(val service: MessageSender) private def handleUpdateBreakoutUsers(msg: UpdateBreakoutUsersOutMessage) { val users = new java.util.ArrayList[BreakoutUserPayload]() msg.users.foreach(x => users.add(new BreakoutUserPayload(x.id, x.name))) - val payload = new UpdateBreakoutUsersPayload(msg.meetingId, msg.breakoutId, users) + val payload = new UpdateBreakoutUsersPayload(msg.parentMeetingId, msg.breakoutMeetingId, users) val request = new UpdateBreakoutUsers(payload) service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson()) } @@ -85,29 +85,29 @@ class JsonMessageSenderActor(val service: MessageSender) private def handleBreakoutRoomsList(msg: BreakoutRoomsListOutMessage) { val rooms = new java.util.ArrayList[BreakoutRoomPayload]() - msg.rooms.foreach(r => rooms.add(new BreakoutRoomPayload(msg.meetingId, r.breakoutId, r.name))) + msg.rooms.foreach(r => rooms.add(new BreakoutRoomPayload(msg.meetingId, r.meetingId, r.externalMeetingId, r.name, r.sequence))) val payload = new BreakoutRoomsListPayload(msg.meetingId, rooms, msg.roomsReady) val request = new BreakoutRoomsList(payload) service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson()) } private def handleCreateBreakoutRoom(msg: CreateBreakoutRoom) { - val payload = new CreateBreakoutRoomRequestPayload(msg.room.breakoutId, msg.room.parentId, msg.room.name, - msg.room.voiceConfId, msg.room.viewerPassword, msg.room.moderatorPassword, + val payload = new CreateBreakoutRoomRequestPayload(msg.room.breakoutMeetingId, msg.room.parentId, msg.room.name, + msg.room.sequence, msg.room.voiceConfId, msg.room.viewerPassword, msg.room.moderatorPassword, msg.room.durationInMinutes, msg.room.sourcePresentationId, msg.room.sourcePresentationSlide, msg.room.record) val request = new CreateBreakoutRoomRequest(payload) service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson()) } private def handleEndBreakoutRoom(msg: EndBreakoutRoom) { - val payload = new EndBreakoutRoomRequestPayload(msg.breakoutId) + val payload = new EndBreakoutRoomRequestPayload(msg.breakoutMeetingId) val request = new EndBreakoutRoomRequest(payload) service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson()) } def handleBreakoutRoomJoinURL(msg: BreakoutRoomJoinURLOutMessage) { - val payload = new BreakoutRoomJoinURLPayload(msg.meetingId, - msg.breakoutId, msg.userId, msg.joinURL) + val payload = new BreakoutRoomJoinURLPayload(msg.parentMeetingId, + msg.breakoutMeetingId, msg.userId, msg.joinURL) val request = new BreakoutRoomJoinURL(payload) service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala index 9514ab22033f8544433e3378d9aa8a03f0455880..63c9f46a6cf3fee23600a7a321bd969f26652c5f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala @@ -12,17 +12,17 @@ import org.bigbluebutton.core.apps.CaptionApp import org.bigbluebutton.core.apps.CaptionModel class LiveMeeting(val mProps: MeetingProperties, - val eventBus: IncomingEventBus, - val outGW: OutMessageGateway, - val chatModel: ChatModel, - val layoutModel: LayoutModel, - val meetingModel: MeetingModel, - val usersModel: UsersModel, - val pollModel: PollModel, - val wbModel: WhiteboardModel, - val presModel: PresentationModel, - val breakoutModel: BreakoutRoomModel, - val captionModel: CaptionModel)(implicit val context: ActorContext) + val eventBus: IncomingEventBus, + val outGW: OutMessageGateway, + val chatModel: ChatModel, + val layoutModel: LayoutModel, + val meetingModel: MeetingModel, + val usersModel: UsersModel, + val pollModel: PollModel, + val wbModel: WhiteboardModel, + val presModel: PresentationModel, + val breakoutModel: BreakoutRoomModel, + val captionModel: CaptionModel)(implicit val context: ActorContext) extends UsersApp with PresentationApp with LayoutApp with ChatApp with WhiteboardApp with PollApp with BreakoutRoomApp with CaptionApp { @@ -56,7 +56,7 @@ class LiveMeeting(val mProps: MeetingProperties, } def startCheckingIfWeNeedToEndVoiceConf() { - if (usersModel.numWebUsers == 0) { + if (usersModel.numWebUsers == 0 && !mProps.isBreakout) { meetingModel.lastWebUserLeft() log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]") } 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 c3a67357ddf95c55d19cc5f7dc4fa4bcf0bdc683..5c8e3dd18e7c7cde37b072440e1f91217b2098a1 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 @@ -37,8 +37,8 @@ class MeetingActorInternal(val mProps: MeetingProperties, if (mProps.isBreakout) { // This is a breakout room. Inform our parent meeting that we have been successfully created. eventBus.publish(BigBlueButtonEvent( - mProps.externalMeetingID, - BreakoutRoomCreated(mProps.externalMeetingID, mProps.meetingID))) + mProps.parentMeetingID, + BreakoutRoomCreated(mProps.parentMeetingID, mProps.meetingID))) } def receive = { 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 241bde0f856baf53663f9f00187ccadfa4a319dc..81509b3aa2d52b34d6e4ba35b67efc4e9b9d9ce1 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 @@ -4,14 +4,15 @@ 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, deskshareBridge: String, duration: Int, autoStartRecording: Boolean, - allowStartStopRecording: Boolean, moderatorPass: String, viewerPass: String, createTime: Long, - createDate: String, red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean) +case class MeetingProperties(meetingID: String, externalMeetingID: String, parentMeetingID: String, meetingName: String, + recorded: Boolean, voiceBridge: String, deskshareBridge: String, duration: Int, + autoStartRecording: Boolean, allowStartStopRecording: Boolean, moderatorPass: String, + viewerPass: String, createTime: Long, createDate: String, + red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean, sequence: Int) case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20, - sendNotice: Boolean = true, sent15MinNotice: Boolean = false, - sent10MinNotice: Boolean = false, sent5MinNotice: Boolean = false) + sendNotice: Boolean = true, sent15MinNotice: Boolean = false, + sent10MinNotice: Boolean = false, sent5MinNotice: Boolean = false) class MeetingModel { private var audioSettingsInited = false diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala index e587c3c24011d4b15c3bd9fefec524eb6627766f..391f888c22911abc84e65d5852d4d5fb6319a091 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala @@ -44,7 +44,6 @@ class MessageSenderActor(val service: MessageSender) extends Actor with ActorLogging { val encoder = new ToJsonEncoder() - def receive = { case msg: UserEjectedFromMeeting => handleUserEjectedFromMeeting(msg) case msg: GetChatHistoryReply => handleGetChatHistoryReply(msg) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala index 2f74e5b5270dd4ac875fc0ed511569187b8018bf..7138d1eb811e579da1e70b3a5a0bce13e8752672 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala @@ -4,7 +4,7 @@ import spray.json.{ DefaultJsonProtocol, JsValue, JsString, DeserializationExcep import org.bigbluebutton.core.api._ object UserMessagesProtocol extends DefaultJsonProtocol { - /* + /* implicit object RoleJsonFormat extends JsonFormat[Role.RoleType] { def write(obj: Role.RoleType): JsValue = JsString(obj.toString) @@ -20,21 +20,20 @@ object UserMessagesProtocol extends DefaultJsonProtocol { def read(json: JsValue): MessageType.MessageType = json match { case JsString(str) => MessageType.withName(str) - case _ => throw new DeserializationException("Enum string expected") + case _ => throw new DeserializationException("Enum string expected") } } - implicit val breakoutRoomInPayloadFormat = jsonFormat2(BreakoutRoomInPayload) - implicit val createBreakoutRoomsFormat = jsonFormat4(CreateBreakoutRooms) + implicit val breakoutRoomInPayloadFormat = jsonFormat3(BreakoutRoomInPayload) + implicit val createBreakoutRoomsFormat = jsonFormat5(CreateBreakoutRooms) implicit val breakoutRoomsListMessageFormat = jsonFormat1(BreakoutRoomsListMessage) - implicit val requestBreakoutJoinURLInMessageFormat = jsonFormat3(RequestBreakoutJoinURLInMessage) + implicit val requestBreakoutJoinURLInMessageFormat = jsonFormat4(RequestBreakoutJoinURLInMessage) implicit val transferUserToMeetingRequestFormat = jsonFormat3(TransferUserToMeetingRequest) implicit val endBreakoutRoomsFormat = jsonFormat1(EndAllBreakoutRooms) implicit val inMsgHeaderFormat = jsonFormat1(InMessageHeader) implicit val outMsgHeaderFormat = jsonFormat1(OutMsgHeader) implicit val outMsgEnvelopeHeaderFormat = jsonFormat2(OutMsgEnvelopeHeader) - implicit val createBreakoutRoomOutMsgPayloadFormat = jsonFormat10(CreateBreakoutRoomOutMsgPayload) + implicit val createBreakoutRoomOutMsgPayloadFormat = jsonFormat11(CreateBreakoutRoomOutMsgPayload) implicit val createBreakoutRoomOutMsgEnvelopePayloadFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelopePayload) implicit val createBreakoutRoomOutMsgEnvelopeFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelope) - } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala index 35e0601bc90ddcd6e5a2016dcc3ecfc136509b48..327c8af5216c84f3460c836f5f1d1527845628f9 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala @@ -7,8 +7,7 @@ trait OutMessage case class CreateBreakoutRoomOutMsgEnvelope(header: OutMsgEnvelopeHeader, payload: CreateBreakoutRoomOutMsgEnvelopePayload) case class CreateBreakoutRoomOutMsgEnvelopePayload(header: OutMsgHeader, payload: CreateBreakoutRoomOutMsgPayload) -case class CreateBreakoutRoomOutMsgPayload(breakoutId: String, name: String, parentId: String, - voiceConfId: String, durationInMinutes: Int, - moderatorPassword: String, viewerPassword: String, - sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean) - +case class CreateBreakoutRoomOutMsgPayload(meetingId: String, parentId: String, name: String, + voiceConfId: String, moderatorPassword: String, viewerPassword: String, + durationInMinutes: Int, sourcePresentationId: String, sourcePresentationSlide: Int, + record: Boolean, sequence: Int) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala index 6ca5af36db6ce5d2cdf79303767852e932a0b2fb..c55149c0dc41ca529eba0662c132ac189eb93905 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala @@ -6,6 +6,8 @@ object Constants { val PAYLOAD = "payload" val MEETING_ID = "meeting_id" val EXTERNAL_MEETING_ID = "external_meeting_id" + val PARENT_MEETING_ID = "parent_meeting_id" + val IS_BREAKOUT = "is_breakout"; val TIMESTAMP = "timestamp" val CURRENT_TIME = "current_time" val USER_ID = "userid" 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 87f553a2ec8d8a36cdc676cf9f0216111979ef27..805e890ac5142d306dca4e314c8cae184553f6ed 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 @@ -42,17 +42,14 @@ case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String, // Sent by user to request the breakout rooms list of a room case class BreakoutRoomsListMessage(meetingId: String) extends InMessage // Sent by user to request creation of breakout rooms -case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int, record: Boolean, - rooms: Vector[BreakoutRoomInPayload]) extends InMessage -case class BreakoutRoomInPayload(name: String, users: Vector[String]) +case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int, record: Boolean, redirectOnJoin: Boolean, rooms: Vector[BreakoutRoomInPayload]) extends InMessage +case class BreakoutRoomInPayload(name: String, sequence: Int, users: Vector[String]) // Sent by user to request for a join URL in order to be able to join a breakout room -case class RequestBreakoutJoinURLInMessage(meetingId: String, breakoutId: String, - userId: String) extends InMessage +case class RequestBreakoutJoinURLInMessage(meetingId: String, breakoutMeetingId: String, userId: String, redirect: Boolean) extends InMessage // Sent by breakout actor to tell meeting actor that breakout room has been created. case class BreakoutRoomCreated(meetingId: String, breakoutRoomId: String) extends InMessage // Sent by breakout actor to tell meeting actor the list of users in the breakout room. -case class BreakoutRoomUsersUpdate(meetingId: String, breakoutId: String, - users: Vector[BreakoutUser]) extends InMessage +case class BreakoutRoomUsersUpdate(meetingId: String, breakoutMeetingId: String, users: Vector[BreakoutUser]) extends InMessage // Send by internal actor to tell the breakout actor to send it's list of users to the main meeting actor. case class SendBreakoutUsersUpdate(meetingId: String) extends InMessage // Sent by user to request ending all the breakout rooms @@ -76,9 +73,9 @@ case class GetLockSettings(meetingID: String, userId: String) extends InMessage ///////////////////////////////////////////////////////////////////////////////// case class ValidateAuthToken(meetingID: String, userId: String, token: String, - correlationId: String, sessionId: String) extends InMessage + correlationId: String, sessionId: String) extends InMessage case class RegisterUser(meetingID: String, userID: String, name: String, role: Role, - extUserID: String, authToken: String, avatarURL: String) extends InMessage + extUserID: String, authToken: String, avatarURL: String) extends InMessage case class UserJoining(meetingID: String, userID: String, authToken: String) extends InMessage case class UserLeaving(meetingID: String, userID: String, sessionId: String) extends InMessage case class GetUsers(meetingID: String, requesterID: String) extends InMessage @@ -100,9 +97,9 @@ case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo case class SendPublicMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage case class SendPrivateMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage case class UserConnectedToGlobalAudio(meetingID: String, /** Not used. Just to satisfy trait **/ voiceConf: String, - userid: String, name: String) extends InMessage + userid: String, name: String) extends InMessage case class UserDisconnectedFromGlobalAudio(meetingID: String, /** Not used. Just to satisfy trait **/ voiceConf: String, - userid: String, name: String) extends InMessage + userid: String, name: String) extends InMessage /////////////////////////////////////////////////////////////////////////////////////// // Layout @@ -111,7 +108,7 @@ case class UserDisconnectedFromGlobalAudio(meetingID: String, /** Not used. Just case class GetCurrentLayoutRequest(meetingID: String, requesterID: String) extends InMessage case class SetLayoutRequest(meetingID: String, requesterID: String, layoutID: String) extends InMessage case class LockLayoutRequest(meetingID: String, setById: String, lock: Boolean, viewersOnly: Boolean, - layout: Option[String]) extends InMessage + layout: Option[String]) extends InMessage case class BroadcastLayoutRequest(meetingID: String, requesterID: String, layout: String) extends InMessage ////////////////////////////////////////////////////////////////////////////////////// @@ -123,19 +120,19 @@ case class RemovePresentation(meetingID: String, presentationID: String) extends case class GetPresentationInfo(meetingID: String, requesterID: String, replyTo: String) extends InMessage case class SendCursorUpdate(meetingID: String, xPercent: Double, yPercent: Double) extends InMessage case class ResizeAndMoveSlide(meetingID: String, xOffset: Double, yOffset: Double, - widthRatio: Double, heightRatio: Double) extends InMessage + widthRatio: Double, heightRatio: Double) extends InMessage case class GotoSlide(meetingID: String, page: String) extends InMessage case class SharePresentation(meetingID: String, presentationID: String, share: Boolean) extends InMessage case class GetSlideInfo(meetingID: String, requesterID: String, replyTo: String) extends InMessage case class PreuploadedPresentations(meetingID: String, presentations: Seq[Presentation]) extends InMessage case class PresentationConversionUpdate(meetingID: String, messageKey: String, code: String, - presentationId: String, presName: String) extends InMessage + presentationId: String, presName: String) extends InMessage case class PresentationPageCountError(meetingID: String, messageKey: String, code: String, presentationId: String, - numberOfPages: Int, maxNumberPages: Int, presName: String) extends InMessage + numberOfPages: Int, maxNumberPages: Int, presName: String) extends InMessage case class PresentationSlideGenerated(meetingID: String, messageKey: String, code: String, presentationId: String, - numberOfPages: Int, pagesCompleted: Int, presName: String) extends InMessage + numberOfPages: Int, pagesCompleted: Int, presName: String) extends InMessage case class PresentationConversionCompleted(meetingID: String, messageKey: String, code: String, - presentation: Presentation) extends InMessage + presentation: Presentation) extends InMessage ///////////////////////////////////////////////////////////////////////////////////// // Polling @@ -164,9 +161,9 @@ case class MuteUserRequest(meetingID: String, requesterID: String, userID: Strin case class LockUserRequest(meetingID: String, requesterID: String, userID: String, lock: Boolean) extends InMessage case class EjectUserFromVoiceRequest(meetingID: String, userId: String, ejectedBy: String) extends InMessage case class VoiceUserJoinedMessage(meetingID: String, user: String, voiceConfId: String, - callerIdNum: String, callerIdName: String, muted: Boolean, talking: Boolean) extends InMessage + callerIdNum: String, callerIdName: String, muted: Boolean, talking: Boolean) extends InMessage case class UserJoinedVoiceConfMessage(voiceConfId: String, voiceUserId: String, userId: String, externUserId: String, - callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean) extends InMessage + callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean) extends InMessage case class UserLeftVoiceConfMessage(voiceConfId: String, voiceUserId: String) extends InMessage case class UserLockedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, locked: Boolean) extends InMessage case class UserMutedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, muted: Boolean) 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 a356e8454784ec92236a7448834d7d2899bf5680..5c0753e492e638736d9f7c7ed05b4b8d9ccb8120 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 @@ -14,8 +14,8 @@ case class VoiceRecordingStarted(meetingID: String, recorded: Boolean, recording case class VoiceRecordingStopped(meetingID: String, recorded: Boolean, recordingFile: String, timestamp: String, confNum: String) extends IOutMessage case class RecordingStatusChanged(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage case class GetRecordingStatusReply(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage -case class MeetingCreated(meetingID: String, externalMeetingID: String, recorded: Boolean, name: String, - voiceBridge: String, duration: Int, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) extends IOutMessage +case class MeetingCreated(meetingID: String, externalMeetingID: String, parentMeetingID: String, recorded: Boolean, name: String, + voiceBridge: String, duration: Int, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String, isBreakout: Boolean) extends IOutMessage case class MeetingMuted(meetingID: String, recorded: Boolean, meetingMuted: Boolean) extends IOutMessage case class MeetingEnded(meetingID: String, recorded: Boolean, voiceBridge: String) extends IOutMessage case class MeetingState(meetingID: String, recorded: Boolean, userId: String, permissions: Permissions, meetingMuted: Boolean) extends IOutMessage @@ -30,17 +30,17 @@ case object IsAliveMessage extends IOutMessage // Breakout Rooms case class BreakoutRoomsListOutMessage(meetingId: String, rooms: Vector[BreakoutRoomBody], roomsReady: Boolean) extends IOutMessage case class CreateBreakoutRoom(meetingId: String, room: BreakoutRoomOutPayload) extends IOutMessage -case class EndBreakoutRoom(breakoutId: String) extends IOutMessage -case class BreakoutRoomOutPayload(breakoutId: String, name: String, parentId: String, +case class EndBreakoutRoom(breakoutMeetingId: String) extends IOutMessage +case class BreakoutRoomOutPayload(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer, voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String, sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean) -case class BreakoutRoomJoinURLOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, userId: String, joinURL: String) extends IOutMessage -case class BreakoutRoomStartedOutMessage(meetingId: String, recorded: Boolean, breakout: BreakoutRoomBody) extends IOutMessage -case class BreakoutRoomBody(name: String, breakoutId: String) -case class UpdateBreakoutUsersOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, users: Vector[BreakoutUser]) extends IOutMessage +case class BreakoutRoomJoinURLOutMessage(parentMeetingId: String, recorded: Boolean, breakoutMeetingId: String, userId: String, joinURL: String) extends IOutMessage +case class BreakoutRoomStartedOutMessage(parentMeetingId: String, recorded: Boolean, breakout: BreakoutRoomBody) extends IOutMessage +case class BreakoutRoomBody(name: String, externalMeetingId: String, meetingId: String, sequence: Int) +case class UpdateBreakoutUsersOutMessage(parentMeetingId: String, recorded: Boolean, breakoutMeetingId: String, users: Vector[BreakoutUser]) extends IOutMessage case class MeetingTimeRemainingUpdate(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage case class BreakoutRoomsTimeRemainingUpdateOutMessage(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage -case class BreakoutRoomEndedOutMessage(meetingId: String, breakoutId: String) extends IOutMessage +case class BreakoutRoomEndedOutMessage(parentMeetingId: String, meetingId: String) extends IOutMessage // Permissions case class PermissionsSettingInitialized(meetingID: String, permissions: Permissions, applyTo: Array[UserVO]) extends IOutMessage diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala index 90a1d236a4a1d1da7b00149f13a4406696a73ddb..2d858d45b9a6c41a938fa3a3313c02c77fc3b896 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala @@ -21,8 +21,10 @@ trait BreakoutRoomApp extends SystemConfiguration { val eventBus: IncomingEventBus def handleBreakoutRoomsList(msg: BreakoutRoomsListMessage) { - val breakoutRooms = breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.id) } - outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms, breakoutModel.pendingRoomsNumber == 0 && breakoutRooms.length > 0)); + val breakoutRooms = breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.externalMeetingId, r.id, r.sequence) } + val roomsReady = breakoutModel.pendingRoomsNumber == 0 && breakoutRooms.length > 0 + log.info("Sending breakout rooms list to {} with containing {} room(s)", mProps.meetingID, breakoutRooms.length) + outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms, roomsReady)) } def handleCreateBreakoutRooms(msg: CreateBreakoutRooms) { @@ -31,18 +33,26 @@ trait BreakoutRoomApp extends SystemConfiguration { log.warning("CreateBreakoutRooms event received while {} are pending to be created for meeting {}", breakoutModel.pendingRoomsNumber, mProps.meetingID) return } + if (breakoutModel.getNumberOfRooms() > 0) { + log.warning("CreateBreakoutRooms event received while {} breakout rooms running for meeting {}", breakoutModel.getNumberOfRooms(), mProps.meetingID) + return + } var i = 0 - val sourcePresentationId = presModel.getCurrentPresentation().get.id - val sourcePresentationSlide = presModel.getCurrentPage().get.num + // in very rare cases the presentation conversion generates an error, what should we do? + // those cases where default.pdf is deleted from the whiteboard + val sourcePresentationId = if (!presModel.getCurrentPresentation().isEmpty) presModel.getCurrentPresentation().get.id else "blank" + val sourcePresentationSlide = if (!presModel.getCurrentPage().isEmpty) presModel.getCurrentPage().get.num else 0 breakoutModel.pendingRoomsNumber = msg.rooms.length; + breakoutModel.redirectOnJoin = msg.redirectOnJoin; for (room <- msg.rooms) { i += 1 - val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mProps.meetingID, i) + val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(mProps.meetingID, i) val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(mProps.voiceBridge, i) - val r = breakoutModel.createBreakoutRoom(breakoutMeetingId, room.name, voiceConfId, room.users) - val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID, + val r = breakoutModel.createBreakoutRoom(mProps.meetingID, breakoutMeetingId._1, breakoutMeetingId._2, room.name, + room.sequence, voiceConfId, room.users) + val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID, r.sequence, r.voiceConfId, msg.durationInMinutes, mProps.moderatorPass, mProps.viewerPass, sourcePresentationId, sourcePresentationSlide, msg.record) outGW.send(new CreateBreakoutRoom(mProps.meetingID, p)) @@ -51,36 +61,37 @@ trait BreakoutRoomApp extends SystemConfiguration { meetingModel.breakoutRoomsStartedOn = timeNowInSeconds; } - def sendJoinURL(userId: String, breakoutId: String) { + def sendJoinURL(userId: String, externalMeetingId: String, redirect: Boolean) { + log.debug("Sending breakout meeting {} Join URL for user: {}", externalMeetingId, userId); for { user <- usersModel.getUser(userId) apiCall = "join" - params = BreakoutRoomsUtil.joinParams(user.name, userId, true, breakoutId, mProps.moderatorPass, true) + params = BreakoutRoomsUtil.joinParams(user.name, userId, true, externalMeetingId, mProps.moderatorPass, redirect) baseString = BreakoutRoomsUtil.createBaseString(params) checksum = BreakoutRoomsUtil.calculateChecksum(apiCall, baseString, bbbWebSharedSecret) joinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, baseString, checksum) - } yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, breakoutId, userId, joinURL)) + } yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, externalMeetingId, userId, joinURL)) } def handleRequestBreakoutJoinURL(msg: RequestBreakoutJoinURLInMessage) { - sendJoinURL(msg.userId, msg.breakoutId) + sendJoinURL(msg.userId, msg.breakoutMeetingId, msg.redirect) } def handleBreakoutRoomCreated(msg: BreakoutRoomCreated) { breakoutModel.pendingRoomsNumber -= 1 val room = breakoutModel.getBreakoutRoom(msg.breakoutRoomId) room foreach { room => - sendBreakoutRoomStarted(mProps.meetingID, room.name, room.id, room.voiceConfId) + sendBreakoutRoomStarted(room.parentRoomId, room.name, room.externalMeetingId, room.id, room.sequence, room.voiceConfId) } - // We avoid sending invitation + // We postpone sending invitation until all breakout rooms have been created if (breakoutModel.pendingRoomsNumber == 0) { log.info("All breakout rooms created for meetingId={}", mProps.meetingID) breakoutModel.getRooms().foreach { room => breakoutModel.getAssignedUsers(room.id) foreach { users => users.foreach { u => - log.debug("Sending Join URL for users: {}", u); - sendJoinURL(u, room.id) + log.debug("Sending Join URL for users"); + sendJoinURL(u, room.externalMeetingId, breakoutModel.redirectOnJoin) } } } @@ -88,8 +99,9 @@ trait BreakoutRoomApp extends SystemConfiguration { } } - def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, breakoutId: String, voiceConfId: String) { - outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, breakoutId))) + def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, externalMeetingId: String, breakoutMeetingId: String, sequence: Int, voiceConfId: String) { + log.info("Sending breakout room started {} for parent meeting {} ", breakoutMeetingId, meetingId); + outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, externalMeetingId, breakoutMeetingId, sequence))) } def handleBreakoutRoomEnded(msg: BreakoutRoomEnded) { @@ -98,16 +110,16 @@ trait BreakoutRoomApp extends SystemConfiguration { } def handleBreakoutRoomUsersUpdate(msg: BreakoutRoomUsersUpdate) { - breakoutModel.updateBreakoutUsers(msg.breakoutId, msg.users) foreach { room => - outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutId, room.users)) + breakoutModel.updateBreakoutUsers(msg.breakoutMeetingId, msg.users) foreach { room => + outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutMeetingId, room.users)) } } def handleSendBreakoutUsersUpdate(msg: SendBreakoutUsersUpdate) { val users = usersModel.getUsers().toVector val breakoutUsers = users map { u => new BreakoutUser(u.externUserID, u.name) } - eventBus.publish(BigBlueButtonEvent(mProps.externalMeetingID, - new BreakoutRoomUsersUpdate(mProps.externalMeetingID, mProps.meetingID, breakoutUsers))) + eventBus.publish(BigBlueButtonEvent(mProps.parentMeetingID, + new BreakoutRoomUsersUpdate(mProps.parentMeetingID, mProps.meetingID, breakoutUsers))) } def handleTransferUserToMeeting(msg: TransferUserToMeetingRequest) { @@ -146,18 +158,18 @@ trait BreakoutRoomApp extends SystemConfiguration { } object BreakoutRoomsUtil { - def createMeetingId(id: String, index: Int): String = { - id.concat("-").concat(index.toString()) + def createMeetingIds(id: String, index: Int): (String, String) = { + val timeStamp = System.currentTimeMillis() + val externalHash = DigestUtils.sha1Hex(id.concat("-").concat(timeStamp.toString()).concat("-").concat(index.toString())) + val externalId = externalHash.concat("-").concat(timeStamp.toString()) + val internalId = DigestUtils.sha1Hex(externalId).concat("-").concat(timeStamp.toString()) + (internalId, externalId) } def createVoiceConfId(id: String, index: Int): String = { id.concat(index.toString()) } - def fromSWFtoPDF(swfURL: String): String = { - swfURL.replace("swf", "pdf") - } - def createJoinURL(webAPI: String, apiCall: String, baseString: String, checksum: String): String = { var apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/") apiURL.concat(apiCall).concat("?").concat(baseString).concat("&checksum=").concat(checksum) @@ -174,13 +186,13 @@ object BreakoutRoomsUtil { checksum(apiCall.concat(baseString).concat(sharedSecret)) } - def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutId: String, + def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutMeetingId: String, password: String, redirect: Boolean): mutable.Map[String, String] = { val params = new collection.mutable.HashMap[String, String] params += "fullName" -> urlEncode(username) - params += "userID" -> urlEncode(userId + "-" + breakoutId.substring(breakoutId.lastIndexOf("-") + 1)); + params += "userID" -> urlEncode(userId + "-" + breakoutMeetingId.substring(breakoutMeetingId.lastIndexOf("-") + 1)); params += "isBreakout" -> urlEncode(isBreakout.toString()) - params += "meetingID" -> urlEncode(breakoutId) + params += "meetingID" -> urlEncode(breakoutMeetingId) params += "password" -> urlEncode(password) params += "redirect" -> urlEncode(redirect.toString()) @@ -218,18 +230,4 @@ object BreakoutRoomsUtil { def urlEncode(s: String): String = { URLEncoder.encode(s, "UTF-8"); } - - // - //encodeURIComponent() -- Java encoding similiar to JavaScript encodeURIComponent - // - def encodeURIComponent(component: String): String = { - URLEncoder.encode(component, "UTF-8") - .replaceAll("\\%28", "(") - .replaceAll("\\%29", ")") - .replaceAll("\\+", "%20") - .replaceAll("\\%27", "'") - .replaceAll("\\%21", "!") - .replaceAll("\\%7E", "~") - } - } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala index 5ec8a3f6d90e739ac5085372cc640cb7cb92e3e3..dbc653480e564f1bd4bc64d596ede0800ef5d02c 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala @@ -1,16 +1,16 @@ package org.bigbluebutton.core.apps -import scala.collection.mutable.ArrayBuffer -import scala.collection.immutable.HashMap +import scala.Vector case class BreakoutUser(id: String, name: String) -case class BreakoutRoom(id: String, name: String, voiceConfId: String, - assignedUsers: Vector[String], users: Vector[BreakoutUser]) +case class BreakoutRoom(id: String, externalMeetingId: String, name: String, parentRoomId: String, sequence: Integer, voiceConfId: String, + assignedUsers: Vector[String], users: Vector[BreakoutUser]) class BreakoutRoomModel { private var rooms = new collection.immutable.HashMap[String, BreakoutRoom] var pendingRoomsNumber: Integer = 0 + var redirectOnJoin: Boolean = false def add(room: BreakoutRoom): BreakoutRoom = { rooms += room.id -> room @@ -21,9 +21,9 @@ class BreakoutRoomModel { rooms -= id } - def createBreakoutRoom(id: String, name: String, voiceConfId: String, - assignedUsers: Vector[String]): BreakoutRoom = { - val room = new BreakoutRoom(id, name, voiceConfId, assignedUsers, Vector()) + def createBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, voiceConfId: String, + assignedUsers: Vector[String]): BreakoutRoom = { + val room = new BreakoutRoom(id, externalMeetingId, name, parentRoomId, sequence, voiceConfId, assignedUsers, Vector()) add(room) } @@ -35,18 +35,21 @@ class BreakoutRoomModel { rooms.values.toArray } - def getAssignedUsers(breakoutId: String): Option[Vector[String]] = { + def getNumberOfRooms(): Int = { + rooms.size + } + + def getAssignedUsers(breakoutMeetingId: String): Option[Vector[String]] = { for { - room <- rooms.get(breakoutId) + room <- rooms.get(breakoutMeetingId) } yield room.assignedUsers } - def updateBreakoutUsers(breakoutId: String, users: Vector[BreakoutUser]): Option[BreakoutRoom] = { + def updateBreakoutUsers(breakoutMeetingId: String, users: Vector[BreakoutUser]): Option[BreakoutRoom] = { for { - room <- rooms.get(breakoutId) + room <- rooms.get(breakoutMeetingId) newroom = room.copy(users = users) room2 = add(newroom) } yield room2 } } - diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala index c8999da668edf644ebfd2ac3b61a993cf5a6e1b1..74f9e05337e74ee3ddbe79b7d3b0b7244cda2ed8 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala @@ -29,6 +29,8 @@ object MeetingMessageToJsonConverter { val payload = new java.util.HashMap[String, Any]() payload.put(Constants.MEETING_ID, msg.meetingID) payload.put(Constants.EXTERNAL_MEETING_ID, msg.externalMeetingID) + payload.put(Constants.PARENT_MEETING_ID, msg.parentMeetingID) + payload.put(Constants.IS_BREAKOUT, msg.isBreakout) payload.put(Constants.NAME, msg.name) payload.put(Constants.RECORDED, msg.recorded) payload.put(Constants.VOICE_CONF, msg.voiceBridge) @@ -145,8 +147,10 @@ object MeetingMessageToJsonConverter { def breakoutRoomStartedOutMessageToJson(msg: BreakoutRoomStartedOutMessage): String = { val payload = new java.util.HashMap[String, Any]() - payload.put("meetingId", msg.meetingId) - payload.put("breakoutId", msg.breakout.breakoutId) + payload.put("meetingId", msg.breakout.meetingId) + payload.put("externalMeetingId", msg.breakout.externalMeetingId) + payload.put("parentMeetingId", msg.parentMeetingId) + payload.put("sequence", msg.breakout.sequence) payload.put("name", msg.breakout.name) val header = Util.buildHeader(BreakoutRoomStarted.NAME, None) @@ -155,8 +159,8 @@ object MeetingMessageToJsonConverter { def breakoutRoomEndedOutMessageToJson(msg: BreakoutRoomEndedOutMessage): String = { val payload = new java.util.HashMap[String, Any]() + payload.put("parentMeetingId", msg.parentMeetingId) payload.put("meetingId", msg.meetingId) - payload.put("breakoutId", msg.breakoutId) val header = Util.buildHeader(BreakoutRoomClosed.NAME, None) Util.buildJson(header, payload) @@ -164,8 +168,8 @@ object MeetingMessageToJsonConverter { def breakoutRoomJoinURLOutMessageToJson(msg: BreakoutRoomJoinURLOutMessage): String = { val payload = new java.util.HashMap[String, Any]() - payload.put("meetingId", msg.meetingId) - payload.put("breakoutId", msg.breakoutId) + payload.put("parentMeetingId", msg.parentMeetingId) + payload.put("breakoutMeetingId", msg.breakoutMeetingId) payload.put("userId", msg.userId) payload.put("joinURL", msg.joinURL) @@ -175,8 +179,8 @@ object MeetingMessageToJsonConverter { def updateBreakoutUsersOutMessageToJson(msg: UpdateBreakoutUsersOutMessage): String = { val payload = new java.util.HashMap[String, Any]() - payload.put("meetingId", msg.meetingId) - payload.put("breakoutId", msg.breakoutId) + payload.put("parentMeetingId", msg.parentMeetingId) + payload.put("breakoutMeetingId", msg.breakoutMeetingId) payload.put("recorded", msg.recorded) payload.put("users", msg.users.toArray) diff --git a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala index 97bf4c31003f97f1260c8d961393c0006bf1a98f..0d2f5850d59a527ab8c4f1252abaad5a7d1164b1 100755 --- a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala +++ b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala @@ -4,6 +4,8 @@ trait AppsTestFixtures { val meetingId = "testMeetingId" val externalMeetingId = "testExternalMeetingId" + val parentMeetingId = "testParentMeetingId" + val sequence = 4 val meetingName = "test meeting" val record = false val voiceConfId = "85115" @@ -16,13 +18,15 @@ trait AppsTestFixtures { val createTime = System.currentTimeMillis val createDate = "Oct 26, 2015" val isBreakout = false + val red5DeskShareIP = "127.0.0.1" + val red5DeskShareApp = "red5App" - val mProps = new MeetingProperties(meetingId, externalMeetingId, + val mProps = new MeetingProperties(meetingId, externalMeetingId, parentMeetingId, meetingName, record, voiceConfId, deskshareConfId, durationInMinutes, autoStartRecording, allowStartStopRecording, moderatorPassword, viewerPassword, - createTime, createDate, isBreakout) - -} \ No newline at end of file + createTime, createDate, red5DeskShareIP, red5DeskShareApp, + isBreakout, sequence) +} diff --git a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala index a84000b5e4b9e8447195cb56e72a06320d702054..c980b35d665eb0aa27c6cf51c8806c864da7d0c0 100755 --- a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala +++ b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala @@ -6,18 +6,11 @@ import org.bigbluebutton.core.UnitSpec class BreakoutRoomsUtilSpec extends UnitSpec { - it should "return a pdfURL" in { - val baseURL = "http://localhost/pre1/page1." - val swfURL = baseURL + "swf" - val pdfURL = BreakoutRoomsUtil.fromSWFtoPDF(swfURL) - assert(pdfURL == baseURL + "pdf") - } - it should "return a meetingId" in { val mainMeetingId = "abc-123" val index = 1 val result = mainMeetingId.concat("-").concat(index.toString()) - val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mainMeetingId, index) + val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(mainMeetingId, index) assert(breakoutMeetingId == result) } diff --git a/akka-bbb-fsesl/build.sbt b/akka-bbb-fsesl/build.sbt index 2f751e0e5de5f8360d93668b9d5a42c5e9aed122..2d99dde59877d0cab3da07d65d523a1909740fd7 100755 --- a/akka-bbb-fsesl/build.sbt +++ b/akka-bbb-fsesl/build.sbt @@ -47,7 +47,7 @@ libraryDependencies ++= { "org.pegdown" % "pegdown" % "1.4.0", "junit" % "junit" % "4.11", "com.etaty.rediscala" %% "rediscala" % "1.4.0", - "commons-codec" % "commons-codec" % "1.8", + "commons-codec" % "commons-codec" % "1.10", "joda-time" % "joda-time" % "2.3", "com.google.code.gson" % "gson" % "1.7.1", "redis.clients" % "jedis" % "2.1.0", diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java index d2ecfc2881d4afaf374117419b8f9b926a7b1c25..48fbdb56281b809d148c5a5c7e6a84ec772cc296 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java @@ -3,48 +3,54 @@ package org.bigbluebutton.messages; import org.bigbluebutton.common.messages.IBigBlueButtonMessage; public class CreateMeetingRequest implements IBigBlueButtonMessage { - public final static String NAME = "CreateMeetingRequest"; - - public final Header header; - public final CreateMeetingRequestPayload payload; - - public CreateMeetingRequest(CreateMeetingRequestPayload payload) { - this.header = new Header(NAME); - this.payload = payload; - } - - public static class CreateMeetingRequestPayload { - public final String id; - public final String externalId; - public final String name; - public final Boolean record; - public final String voiceConfId; - public final Integer durationInMinutes; - public final Boolean autoStartRecording; - public final Boolean allowStartStopRecording; - public final String moderatorPassword; - public final String viewerPassword; - public final Long createTime; - public final String createDate; - public final Boolean isBreakout; - - public CreateMeetingRequestPayload(String id, String externalId, String name, Boolean record, String voiceConfId, - Integer duration, Boolean autoStartRecording, - Boolean allowStartStopRecording, String moderatorPass, - String viewerPass, Long createTime, String createDate, Boolean isBreakout) { - this.id = id; - this.externalId = externalId; - this.name = name; - this.record = record; - this.voiceConfId = voiceConfId; - this.durationInMinutes = duration; - this.autoStartRecording = autoStartRecording; - this.allowStartStopRecording = allowStartStopRecording; - this.moderatorPassword = moderatorPass; - this.viewerPassword = viewerPass; - this.createTime = createTime; - this.createDate = createDate; - this.isBreakout = isBreakout; + public final static String NAME = "CreateMeetingRequest"; + + public final Header header; + public final CreateMeetingRequestPayload payload; + + public CreateMeetingRequest(CreateMeetingRequestPayload payload) { + this.header = new Header(NAME); + this.payload = payload; + } + + public static class CreateMeetingRequestPayload { + public final String id; + public final String externalId; + public final String parentId; + public final String name; + public final Boolean record; + public final String voiceConfId; + public final Integer durationInMinutes; + public final Boolean autoStartRecording; + public final Boolean allowStartStopRecording; + public final String moderatorPassword; + public final String viewerPassword; + public final Long createTime; + public final String createDate; + public final Boolean isBreakout; + public final Integer sequence; + + public CreateMeetingRequestPayload(String id, String externalId, + String parentId, String name, Boolean record, + String voiceConfId, Integer duration, + Boolean autoStartRecording, Boolean allowStartStopRecording, + String moderatorPass, String viewerPass, Long createTime, + String createDate, Boolean isBreakout, Integer sequence) { + this.id = id; + this.externalId = externalId; + this.parentId = parentId; + this.name = name; + this.record = record; + this.voiceConfId = voiceConfId; + this.durationInMinutes = duration; + this.autoStartRecording = autoStartRecording; + this.allowStartStopRecording = allowStartStopRecording; + this.moderatorPassword = moderatorPass; + this.viewerPassword = viewerPass; + this.createTime = createTime; + this.createDate = createDate; + this.isBreakout = isBreakout; + this.sequence = sequence; + } } - } } diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java index 406a6c1a543f51d3a618ba89e69212c4bb37f480..20509fba10c478e9bbd4c419c4405c14146aebcc 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java @@ -20,14 +20,14 @@ package org.bigbluebutton.messages.payload; public class BreakoutRoomJoinURLPayload { - public final String meetingId; - public final String breakoutId; + public final String parentMeetingId; + public final String breakoutMeetingId; public final String userId; public final String joinURL; - public BreakoutRoomJoinURLPayload(String meetingId, String breakoutId, String userId, String joinURL) { - this.meetingId = meetingId; - this.breakoutId = breakoutId; + public BreakoutRoomJoinURLPayload(String parentMeetingId, String breakoutMeetingId, String userId, String joinURL) { + this.parentMeetingId = parentMeetingId; + this.breakoutMeetingId = breakoutMeetingId; this.userId = userId; this.joinURL = joinURL; } diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java index b21d95e8368a7dd9b59fd7558cd6349ce19ef495..898ab5048690b8fbd91aedcabed86f87af040891 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java @@ -2,13 +2,18 @@ package org.bigbluebutton.messages.payload; public class BreakoutRoomPayload { - public final String meetingId; - public final String breakoutId; - public final String name; + public final String parentMeetingId; + public final String meetingId; + public final String externalMeetingId; + public final String name; + public final Integer sequence; - public BreakoutRoomPayload(String meetingId, String breakoutId, String name) { - this.meetingId = meetingId; - this.breakoutId = breakoutId; - this.name = name; - } + public BreakoutRoomPayload(String parentMeetingId, String meetingId, + String externalMeetingId, String name, Integer sequence) { + this.parentMeetingId = parentMeetingId; + this.meetingId = meetingId; + this.externalMeetingId = externalMeetingId; + this.name = name; + this.sequence = sequence; + } } diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java index 87336f3f3dca54be00820e671bdbbd986932636b..20cceb76bcdd1b3b587513536bdcbc17338cbb37 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java @@ -3,13 +3,16 @@ package org.bigbluebutton.messages.payload; import java.util.ArrayList; public class BreakoutRoomRequestPayload { - // Name of the breakout room - public final String name; - // List of user ids to assign to the breakout room - public final ArrayList<String> users; - - public BreakoutRoomRequestPayload(String name, ArrayList<String> users) { - this.name = name; - this.users = users; - } + // Name of the breakout room + public final String name; + // Sequence of the breakout room + public final Integer sequence; + // List of user ids to assign to the breakout room + public final ArrayList<String> users; + + public BreakoutRoomRequestPayload(String name, Integer sequence, ArrayList<String> users) { + this.name = name; + this.sequence = sequence; + this.users = users; + } } diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java index b69987cd9de4b54f3173db64558254b65a62087e..9c49157c76b5ee750a8b01d3707fce95ede2682d 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java @@ -1,9 +1,10 @@ package org.bigbluebutton.messages.payload; public class CreateBreakoutRoomRequestPayload { - public final String breakoutId; - public final String parentId; // The main meeting internal id + public final String breakoutMeetingId; + public final String parentMeetingId; // The main meeting internal id public final String name; // The name of the breakout room + public final Integer sequence; // The sequnce number of the breakout room public final String voiceConfId; // The voice conference id public final String viewerPassword; public final String moderatorPassword; @@ -12,14 +13,15 @@ public class CreateBreakoutRoomRequestPayload { public final Integer sourcePresentationSlide; public final Boolean record; - public CreateBreakoutRoomRequestPayload(String breakoutId, String parentId, - String name, String voiceConfId, String viewerPassword, + public CreateBreakoutRoomRequestPayload(String meetingMeetingId, String parentMeetingId, + String name, Integer sequence, String voiceConfId, String viewerPassword, String moderatorPassword, Integer duration, String sourcePresentationId, Integer sourcePresentationSlide, Boolean record) { - this.breakoutId = breakoutId; - this.parentId = parentId; + this.breakoutMeetingId = meetingMeetingId; + this.parentMeetingId = parentMeetingId; this.name = name; + this.sequence = sequence; this.voiceConfId = voiceConfId; this.viewerPassword = viewerPassword; this.moderatorPassword = moderatorPassword; diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java index 33e020df1994b07b35bf421856d3c6d3667ed935..35038b6e0cf8e17d4a8a75bb6a58acf0d4d23f62 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java @@ -3,21 +3,24 @@ package org.bigbluebutton.messages.payload; import java.util.ArrayList; public class CreateBreakoutRoomsRequestPayload { - // The main meeting internal id - public final String meetingId; - // The list of breakout rooms - public final ArrayList<BreakoutRoomRequestPayload> rooms; - // The duration of the breakout room - public final Integer durationInMinutes; - // Breakout rooms recording option - public final Boolean record; + // The main meeting internal id + public final String meetingId; + // The list of breakout rooms + public final ArrayList<BreakoutRoomRequestPayload> rooms; + // The duration of the breakout room + public final Integer durationInMinutes; + // Breakout rooms recording option + public final Boolean record; + // Creates join URL with redirect value true or false + public final Boolean redirectOnJoin; - public CreateBreakoutRoomsRequestPayload(String meetingId, - ArrayList<BreakoutRoomRequestPayload> breakoutRooms, - Integer duration, Boolean record) { - this.meetingId = meetingId; - this.rooms = breakoutRooms; - this.durationInMinutes = duration; - this.record = record; - } + public CreateBreakoutRoomsRequestPayload(String meetingId, + ArrayList<BreakoutRoomRequestPayload> breakoutRooms, + Integer duration, Boolean record, Boolean redirectOnJoin) { + this.meetingId = meetingId; + this.rooms = breakoutRooms; + this.durationInMinutes = duration; + this.record = record; + this.redirectOnJoin = redirectOnJoin; + } } diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java index 1d3d3c51aa60020e112304bfee0cc9362a048532..aeed47d5185e8601a8111fb55ce66a0fcbb6cb4f 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java @@ -2,14 +2,14 @@ package org.bigbluebutton.messages.payload; public class ListenInOnBreakoutPayload { - public final String meetingId; - public final String targetMeetingId; - public final String userId; + public final String meetingId; + public final String targetMeetingId; + public final String userId; - public ListenInOnBreakoutPayload(String meetingId, String breakoutId, - String userId) { - this.meetingId = meetingId; - this.targetMeetingId = breakoutId; - this.userId = userId; - } + public ListenInOnBreakoutPayload(String meetingId, String targetMeetingId, + String userId) { + this.meetingId = meetingId; + this.targetMeetingId = targetMeetingId; + this.userId = userId; + } } diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java index 7c698c6c89c4bc1c885f4bf6dd992ce046fffc2e..00f2aabaee9e2cd121cca2eea2ba3609799bc431 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java @@ -2,13 +2,16 @@ package org.bigbluebutton.messages.payload; public class RequestBreakoutJoinURLPayload { - public final String meetingId; - public final String breakoutId; - public final String userId; - - public RequestBreakoutJoinURLPayload(String meetingId, String breakoutId, String userId) { - this.meetingId = meetingId; - this.breakoutId = breakoutId; - this.userId = userId; - } + public final String meetingId; + public final String breakoutMeetingId; + public final String userId; + public final Boolean redirect; + + public RequestBreakoutJoinURLPayload(String meetingId, + String breakoutMeetingId, String userId, Boolean redirect) { + this.meetingId = meetingId; + this.breakoutMeetingId = breakoutMeetingId; + this.userId = userId; + this.redirect = redirect; + } } diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java index f96d2ca064d988135350b64710a4b32cb9ce6c06..84810e44cacf905f81d284cc1f576caa472904b8 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java @@ -5,12 +5,12 @@ import java.util.ArrayList; public class UpdateBreakoutUsersPayload { public final ArrayList<BreakoutUserPayload> users; - public final String breakoutId; - public final String meetingId; + public final String breakoutMeetingId; + public final String parentMeetingId; - public UpdateBreakoutUsersPayload(String meetingId, String breakoutId, ArrayList<BreakoutUserPayload> users) { - this.meetingId = meetingId; - this.breakoutId = breakoutId; + public UpdateBreakoutUsersPayload(String meetingParentId, String breakoutMeetingId, ArrayList<BreakoutUserPayload> users) { + this.parentMeetingId = meetingParentId; + this.breakoutMeetingId = breakoutMeetingId; this.users = users; } } diff --git a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java index e520494a35dd8a90c9803282da12accc68d1dc8e..59f3086d0d8f12afa9180afc45bf37aa26178606 100755 --- a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java +++ b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java @@ -13,6 +13,7 @@ public class CreateBreakoutRoomRequestTest { String parentId = "abc-123"; Integer durationInMinutes = 20; String name = "Breakout room 1"; + Integer sequence = 3; String voiceConfId = "851153"; String viewerPassword = "vp"; String moderatorPassword = "mp"; @@ -20,9 +21,10 @@ public class CreateBreakoutRoomRequestTest { Integer sourePresentationSlide = 5; Boolean record = false; - CreateBreakoutRoomRequestPayload payload = - new CreateBreakoutRoomRequestPayload(breakoutId, parentId, name, voiceConfId, - viewerPassword, moderatorPassword, durationInMinutes, sourcePresentationId, sourePresentationSlide, record); + CreateBreakoutRoomRequestPayload payload = new CreateBreakoutRoomRequestPayload( + breakoutId, parentId, name, sequence, voiceConfId, + viewerPassword, moderatorPassword, durationInMinutes, + sourcePresentationId, sourePresentationSlide, record); CreateBreakoutRoomRequest msg = new CreateBreakoutRoomRequest(payload); Gson gson = new Gson(); String json = gson.toJson(msg); @@ -31,8 +33,9 @@ public class CreateBreakoutRoomRequestTest { CreateBreakoutRoomRequest rxMsg = gson.fromJson(json, CreateBreakoutRoomRequest.class); Assert.assertEquals(rxMsg.header.name, CreateBreakoutRoomRequest.NAME); - Assert.assertEquals(rxMsg.payload.breakoutId, breakoutId); + Assert.assertEquals(rxMsg.payload.breakoutMeetingId, breakoutId); Assert.assertEquals(rxMsg.payload.name, name); + Assert.assertEquals(rxMsg.payload.sequence, sequence); Assert.assertEquals(rxMsg.payload.voiceConfId, voiceConfId); Assert.assertEquals(rxMsg.payload.viewerPassword, viewerPassword); Assert.assertEquals(rxMsg.payload.moderatorPassword, moderatorPassword); diff --git a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java index 48c91e0b9f6e77aec85f8d6e62c35c2a08146a03..c7ee9eb2534043f4b73ebf727c5d6132147c883c 100755 --- a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java +++ b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java @@ -15,23 +15,24 @@ public class CreateBreakoutRoomsRequestTest { String meetingId = "abc123"; Integer durationInMinutes = 20; Boolean record = true; + Boolean redirectOnJoin = false; ArrayList<String> room1Users = new ArrayList<String>(); room1Users.add("Tidora"); room1Users.add("Nidora"); room1Users.add("Tinidora"); - BreakoutRoomRequestPayload room1 = new BreakoutRoomRequestPayload("room1", room1Users); + BreakoutRoomRequestPayload room1 = new BreakoutRoomRequestPayload("room1", 1, room1Users); ArrayList<String> room2Users = new ArrayList<String>(); room2Users.add("Jose"); room2Users.add("Wally"); room2Users.add("Paolo"); - BreakoutRoomRequestPayload room2= new BreakoutRoomRequestPayload("room2", room2Users); + BreakoutRoomRequestPayload room2= new BreakoutRoomRequestPayload("room2", 2, room2Users); ArrayList<String> room3Users = new ArrayList<String>(); room3Users.add("Alden"); room3Users.add("Yaya Dub"); - BreakoutRoomRequestPayload room3= new BreakoutRoomRequestPayload("room3", room3Users); + BreakoutRoomRequestPayload room3= new BreakoutRoomRequestPayload("room3", 3, room3Users); ArrayList<BreakoutRoomRequestPayload> rooms = new ArrayList<BreakoutRoomRequestPayload>(); rooms.add(room1); rooms.add(room2); rooms.add(room3); - CreateBreakoutRoomsRequestPayload payload = new CreateBreakoutRoomsRequestPayload(meetingId, rooms, durationInMinutes, record); + CreateBreakoutRoomsRequestPayload payload = new CreateBreakoutRoomsRequestPayload(meetingId, rooms, durationInMinutes, record, redirectOnJoin); CreateBreakoutRoomsRequest msg = new CreateBreakoutRoomsRequest(payload); Gson gson = new Gson(); String json = gson.toJson(msg); diff --git a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java index 476f8efbe2fd05078925a1bb86d82ca4ce846408..e25dfa81c73e13029390fb6fa69acee8a1879b46 100755 --- a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java +++ b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java @@ -12,6 +12,7 @@ public class CreateMeetingRequestTest { public void testCreateMeetingRequest() { String meetingId = "abc123"; String externalId = "extabc123"; + String parentId = ""; Boolean record = false; Integer durationInMinutes = 20; String name = "Breakout room 1"; @@ -19,16 +20,17 @@ public class CreateMeetingRequestTest { Boolean autoStartRecording = false; Boolean allowStartStopRecording = false; Boolean isBreakout = true; + Integer sequence = 4; String viewerPassword = "vp"; String moderatorPassword = "mp"; long createTime = System.currentTimeMillis(); String createDate = new Date(createTime).toString(); - CreateMeetingRequestPayload payload = - new CreateMeetingRequestPayload(meetingId, externalId, name, record, voiceConfId, - durationInMinutes, autoStartRecording, - allowStartStopRecording, moderatorPassword, - viewerPassword, createTime, createDate, isBreakout); + CreateMeetingRequestPayload payload = new CreateMeetingRequestPayload( + meetingId, externalId, parentId, name, record, voiceConfId, + durationInMinutes, autoStartRecording, allowStartStopRecording, + moderatorPassword, viewerPassword, createTime, createDate, + isBreakout, sequence); CreateMeetingRequest msg = new CreateMeetingRequest(payload); Gson gson = new Gson(); String json = gson.toJson(msg); @@ -38,11 +40,14 @@ public class CreateMeetingRequestTest { Assert.assertEquals(rxMsg.header.name, CreateMeetingRequest.NAME); Assert.assertEquals(rxMsg.payload.id, meetingId); + Assert.assertEquals(rxMsg.payload.externalId, externalId); + Assert.assertEquals(rxMsg.payload.parentId, parentId); Assert.assertEquals(rxMsg.payload.name, name); Assert.assertEquals(rxMsg.payload.voiceConfId, voiceConfId); Assert.assertEquals(rxMsg.payload.viewerPassword, viewerPassword); Assert.assertEquals(rxMsg.payload.moderatorPassword, moderatorPassword); Assert.assertEquals(rxMsg.payload.durationInMinutes, durationInMinutes); Assert.assertEquals(rxMsg.payload.isBreakout, isBreakout); + Assert.assertEquals(rxMsg.payload.sequence, sequence); } } diff --git a/bbb-screenshare/app/build.sbt b/bbb-screenshare/app/build.sbt index c1a489f77218274e048177bbdcf3600de18b039b..5e3ad2fc20115b55dfdaf42b2acceb2b262f2a34 100644 --- a/bbb-screenshare/app/build.sbt +++ b/bbb-screenshare/app/build.sbt @@ -44,7 +44,7 @@ libraryDependencies ++= { // "org.pegdown" % "pegdown" % "1.4.0", // "junit" % "junit" % "4.11", // "com.etaty.rediscala" %% "rediscala" % "1.4.0", - "commons-codec" % "commons-codec" % "1.8", + "commons-codec" % "commons-codec" % "1.10", "redis.clients" % "jedis" % "2.7.2", // "org.apache.commons" % "commons-lang3" % "3.2", "org.apache.commons" % "commons-pool2" % "2.3", diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java index 57784c825f445152fabc8c0cfe3f60a7d46eccfe..b94374355f5757a73aa374c67a10442eeb69a222 100644 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java @@ -3,12 +3,11 @@ package org.bigbluebutton.red5.client; import java.util.HashMap; import java.util.Map; -import org.bigbluebutton.common.messages.ChatKeyUtil; -import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage; import org.bigbluebutton.common.messages.DeskShareNotifyASingleViewerEventMessage; +import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage; import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage; -import org.bigbluebutton.red5.client.messaging.DirectClientMessage; import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService; +import org.bigbluebutton.red5.client.messaging.DirectClientMessage; import com.google.gson.JsonObject; import com.google.gson.JsonParser; diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java index ec68c343948d21e7aa09531088897b3bc22851d3..7990274ade0d71bc58c8227845de825f1da21f6f 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java @@ -44,7 +44,6 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; - public class UserClientMessageSender { private static Logger log = Red5LoggerFactory.getLogger(UserClientMessageSender.class, "bigbluebutton"); @@ -543,8 +542,8 @@ public class UserClientMessageSender { private void processBreakoutRoomJoinURL(BreakoutRoomJoinURL msg) { Map<String, Object> args = new HashMap<String, Object>(); - args.put("meetingId", msg.payload.meetingId); - args.put("breakoutId", msg.payload.breakoutId); + args.put("parentMeetingId", msg.payload.parentMeetingId); + args.put("breakoutMeetingId", msg.payload.breakoutMeetingId); args.put("userId", msg.payload.userId); args.put("joinURL", msg.payload.joinURL); @@ -552,7 +551,7 @@ public class UserClientMessageSender { Gson gson = new Gson(); message.put("msg", gson.toJson(args)); - DirectClientMessage m = new DirectClientMessage(msg.payload.meetingId, msg.payload.userId, "breakoutRoomJoinURL", message); + DirectClientMessage m = new DirectClientMessage(msg.payload.parentMeetingId, msg.payload.userId, "breakoutRoomJoinURL", message); service.sendMessage(m); } @@ -584,42 +583,44 @@ public class UserClientMessageSender { private void processUpdateBreakoutUsers(UpdateBreakoutUsers msg) { Map<String, Object> args = new HashMap<String, Object>(); - args.put("meetingId", msg.payload.meetingId); - args.put("breakoutId", msg.payload.breakoutId); + args.put("parentMeetingId", msg.payload.parentMeetingId); + args.put("breakoutMeetingId", msg.payload.breakoutMeetingId); args.put("users", msg.payload.users); Map<String, Object> message = new HashMap<String, Object>(); Gson gson = new Gson(); message.put("msg", gson.toJson(args)); - BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.meetingId, "updateBreakoutUsers", message); + BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.parentMeetingId, "updateBreakoutUsers", message); service.sendMessage(m); } private void processBreakoutRoomStarted(BreakoutRoomStarted msg) { Map<String, Object> args = new HashMap<String, Object>(); - args.put("breakoutId", msg.payload.breakoutId); - args.put("meetingId", msg.payload.meetingId); + args.put("breakoutMeetingId", msg.payload.meetingId); + args.put("parentMeetingId", msg.payload.parentMeetingId); + args.put("externalMeetingId", msg.payload.externalMeetingId); + args.put("sequence", msg.payload.sequence); args.put("name", msg.payload.name); Map<String, Object> message = new HashMap<String, Object>(); Gson gson = new Gson(); message.put("msg", gson.toJson(args)); - BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.meetingId, "breakoutRoomStarted", message); + BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.parentMeetingId, "breakoutRoomStarted", message); service.sendMessage(m); } private void processBreakoutRoomClosed(BreakoutRoomClosed msg) { Map<String, Object> args = new HashMap<String, Object>(); - args.put("breakoutId", msg.payload.breakoutId); - args.put("meetingId", msg.payload.meetingId); + args.put("breakoutMeetingId", msg.payload.meetingId); + args.put("parentMeetingId", msg.payload.parentMeetingId); Map<String, Object> message = new HashMap<String, Object>(); Gson gson = new Gson(); message.put("msg", gson.toJson(args)); - BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.meetingId, "breakoutRoomClosed", message); + BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.parentMeetingId, "breakoutRoomClosed", message); service.sendMessage(m); } } diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java index 510f88bcea1efbce2fe7a6ff047d675baecfed72..792ee0645a08f01b500d3036f4185f35071eba9a 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java @@ -1,6 +1,5 @@ package org.bigbluebutton.red5.client; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java index f16fdc33c75c1f0f97cca070b51c98f432d42b53..fa03834a876d1be42897c45306306a6aab7b5591 100644 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java @@ -18,9 +18,6 @@ */ package org.bigbluebutton.red5.service; -import java.util.HashMap; -import java.util.Map; - import org.bigbluebutton.red5.BigBlueButtonSession; import org.bigbluebutton.red5.Constants; import org.bigbluebutton.red5.pubsub.MessagePublisher; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as index c7201daa9d1c38d9326884dc92ab0cfd68ee8bd3..58ac6998b144bf6ecd1edf1090fe4ae90543d76a 100644 --- a/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as @@ -39,7 +39,9 @@ package org.bigbluebutton.main.events { public var meetingId:String; - public var breakoutId:String; + public var breakoutMeetingId:String; + + public var breakoutMeetingSequence:int; public var rooms:Array; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as index 7d302e8339b4846f212755ae4de4126d9446b774..4623400cb483865f569433bb08d5bfb5a35a9731 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as @@ -334,12 +334,11 @@ package org.bigbluebutton.main.model.users private var _breakoutRooms : Array = []; [Bindable("displayNameChange")] - public function get displayName() : String { - if (ArrayUtils.isEmpty(_breakoutRooms)){ + public function get displayName():String { + if (ArrayUtils.isEmpty(_breakoutRooms)) { return name; - } - else { - return "[" + _breakoutRooms.join(",") + "] " +name; + } else { + return "[" + _breakoutRooms.join(",") + "] " + name; } } @@ -352,14 +351,14 @@ package org.bigbluebutton.main.model.users dispatchEvent(new Event("displayNameChange")); } - public function addBreakoutRoom(roomNumber:String):void { + public function addBreakoutRoom(roomNumber:int):void { if (!ArrayUtils.contains(_breakoutRooms, roomNumber)) { _breakoutRooms.push(roomNumber); dispatchEvent(new Event("displayNameChange")); } } - public function removeBreakoutRoom(roomNumber:String):void { + public function removeBreakoutRoom(roomNumber:int):void { _breakoutRooms.splice(_breakoutRooms.indexOf(roomNumber), 1); dispatchEvent(new Event("displayNameChange")); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as index b94a55ee3d22d39cf4bd5e058422b1555e65a572..ed653efc1343a62c351ef171e16f356ef9a9e069 100644 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as @@ -28,7 +28,11 @@ package org.bigbluebutton.main.model.users { public static const OTHER:String = "other"; - public var breakoutId:String; + public var externalMeetingId:String; + + public var meetingId:String; + + public var sequence:int; public var name:String; @@ -44,6 +48,5 @@ package org.bigbluebutton.main.model.users { public function get numberOfUsers():int { return users.length; } - } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as index c899cfc0d009a9c63fdb8936b8abaaf2eb3d4247..61022aa41a66fe7c82fe9f8a4cc8a44d0b5f4744 100644 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as @@ -20,6 +20,7 @@ package org.bigbluebutton.main.model.users { import mx.collections.ArrayCollection; import mx.collections.Sort; + import mx.collections.SortField; import org.as3commons.lang.ArrayUtils; import org.as3commons.lang.StringUtils; @@ -534,59 +535,87 @@ package org.bigbluebutton.main.model.users { /* Breakout room feature */ public function addBreakoutRoom(newRoom:BreakoutRoom):void { - if (hasBreakoutRoom(newRoom.breakoutId)) { - removeBreakoutRoom(newRoom.breakoutId); + if (hasBreakoutRoom(newRoom.meetingId)) { + removeBreakoutRoom(newRoom.meetingId); } breakoutRooms.addItem(newRoom); + sortBreakoutRooms(); + } + + private function sortBreakoutRooms() : void { + var sort:Sort = new Sort(); + sort.fields = [new SortField("sequence", true, false, true)]; + breakoutRooms.sort = sort; breakoutRooms.refresh(); } - public function updateBreakoutRoomUsers(breakoutId:String, breakoutUsers:Array):void { - var room:Object = getBreakoutRoom(breakoutId); + public function updateBreakoutRoomUsers(breakoutMeetingId:String, breakoutUsers:Array):void { + var room:BreakoutRoom = getBreakoutRoom(breakoutMeetingId); if (room != null) { - BreakoutRoom(room).users = new ArrayCollection(breakoutUsers); - var breakoutRoomNumber:String = StringUtils.substringAfterLast(breakoutId, "-"); + room.users = new ArrayCollection(breakoutUsers); var updateUsers:Array = []; // Update users breakout rooms - var user : BBBUser; + var user:BBBUser; for (var i:int = 0; i < breakoutUsers.length; i++) { var userId:String = StringUtils.substringBeforeLast(breakoutUsers[i].id, "-"); user = getUser(userId); if (user) { - user.addBreakoutRoom(breakoutRoomNumber) + user.addBreakoutRoom(room.sequence) } updateUsers.push(userId); } // Remove users breakout rooms if the users left the breakout rooms for (var j:int = 0; j < users.length; j++) { user = BBBUser(users.getItemAt(j)); - if (updateUsers.indexOf(BBBUser(users.getItemAt(j)).userID) == -1 && ArrayUtils.contains(user.breakoutRooms, breakoutRoomNumber)) { - user.removeBreakoutRoom(breakoutRoomNumber); + if (updateUsers.indexOf(BBBUser(users.getItemAt(j)).userID) == -1 && ArrayUtils.contains(user.breakoutRooms, room.sequence)) { + user.removeBreakoutRoom(room.sequence); } } users.refresh(); } } - + /** - * Returns a breakout room by its breakoutId + * Returns a breakout room by its internal meeting ID */ - public function getBreakoutRoom(breakoutId:String):BreakoutRoom { - var r:Object = getBreakoutRoomIndex(breakoutId); + public function getBreakoutRoom(breakoutMeetingId:String):BreakoutRoom { + var r:Object = getBreakoutRoomIndex(breakoutMeetingId); if (r != null) { return r.room as BreakoutRoom; } return null; } + public function getBreakoutRoomByExternalId(externalId:String):BreakoutRoom { + var aRoom:BreakoutRoom; + for (var i:int = 0; i < breakoutRooms.length; i++) { + aRoom = breakoutRooms.getItemAt(i) as BreakoutRoom; + if (aRoom.externalMeetingId == externalId) { + return aRoom; + } + } + return null; + } + + public function getBreakoutRoomBySequence(sequence:int):BreakoutRoom { + var aRoom:BreakoutRoom; + for (var i:int = 0; i < breakoutRooms.length; i++) { + aRoom = breakoutRooms.getItemAt(i) as BreakoutRoom; + if (aRoom.sequence == sequence) { + return aRoom; + } + } + return null; + } + /** - * Finds the index of a breakout room by its breakoutId + * Finds the index of a breakout room by its internal meeting ID */ - public function getBreakoutRoomIndex(breakoutId:String):Object { + public function getBreakoutRoomIndex(breakoutMeetingId:String):Object { var aRoom:BreakoutRoom; for (var i:int = 0; i < breakoutRooms.length; i++) { aRoom = breakoutRooms.getItemAt(i) as BreakoutRoom; - if (aRoom.breakoutId == breakoutId) { + if (aRoom.meetingId == breakoutMeetingId) { return {index: i, room: aRoom}; } } @@ -594,39 +623,38 @@ package org.bigbluebutton.main.model.users { return null; } - public function removeBreakoutRoom(breakoutId:String):void { - var p:Object = getBreakoutRoomIndex(breakoutId); - if (p != null) { - breakoutRooms.removeItemAt(p.index); - breakoutRooms.refresh(); + public function removeBreakoutRoom(breakoutMeetingId:String):void { + var room:Object = getBreakoutRoomIndex(breakoutMeetingId); + if (room != null) { + breakoutRooms.removeItemAt(room.index); + sortBreakoutRooms(); if (breakoutRooms.length == 0) { breakoutRoomsReady = false; } // Remove breakout room number display from users for (var i:int; i < users.length; i++) { - var breakoutRoomNumber:String = StringUtils.substringAfterLast(breakoutId, "-"); - if (ArrayUtils.contains(users[i].breakoutRooms, breakoutRoomNumber)) { - users[i].removeBreakoutRoom(breakoutRoomNumber); + if (ArrayUtils.contains(users[i].breakoutRooms, room.room.sequence)) { + users[i].removeBreakoutRoom(room.room.sequence); } } users.refresh(); } } - - public function hasBreakoutRoom(breakoutId:String):Boolean { - var p:Object = getBreakoutRoomIndex(breakoutId); + + public function hasBreakoutRoom(breakoutMeetingId:String):Boolean { + var p:Object = getBreakoutRoomIndex(breakoutMeetingId); if (p != null) { return true; } return false; } - public function setBreakoutRoomInListen(listen:Boolean, breakoutId:String):void { + public function setBreakoutRoomInListen(listen:Boolean, breakoutMeetingId:String):void { for (var i:int = 0; i < breakoutRooms.length; i++) { var br:BreakoutRoom = BreakoutRoom(breakoutRooms.getItemAt(i)); if (listen == false) { br.listenStatus = BreakoutRoom.NONE; - } else if (listen == true && br.breakoutId == breakoutId) { + } else if (listen == true && br.meetingId == breakoutMeetingId) { br.listenStatus = BreakoutRoom.SELF; } else { br.listenStatus = BreakoutRoom.OTHER; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as index c7900216e497b9cdfa2901330fa3a527e611055d..67196559c60392a470b971955fc5092738d5bc84 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as @@ -199,20 +199,20 @@ package org.bigbluebutton.main.model.users } public function createBreakoutRooms(e:BreakoutRoomEvent):void{ - sender.createBreakoutRooms(_conferenceParameters.meetingID, e.rooms, e.durationInMinutes, e.record); + sender.createBreakoutRooms(_conferenceParameters.meetingID, e.rooms, e.durationInMinutes, e.record, true); } public function requestBreakoutJoinUrl(e:BreakoutRoomEvent):void{ - sender.requestBreakoutJoinUrl(_conferenceParameters.meetingID, e.breakoutId, e.userId); + sender.requestBreakoutJoinUrl(_conferenceParameters.meetingID, e.breakoutMeetingId, e.userId, true); } public function listenInOnBreakout(e:BreakoutRoomEvent):void { if (e.listen) { - sender.listenInOnBreakout(_conferenceParameters.meetingID, e.breakoutId, _conferenceParameters.userid); + sender.listenInOnBreakout(_conferenceParameters.meetingID, e.breakoutMeetingId, _conferenceParameters.userid); } else { - sender.listenInOnBreakout(e.breakoutId, _conferenceParameters.meetingID, _conferenceParameters.userid); + sender.listenInOnBreakout(e.breakoutMeetingId, _conferenceParameters.meetingID, _conferenceParameters.userid); } - UserManager.getInstance().getConference().setBreakoutRoomInListen(e.listen, e.breakoutId); + UserManager.getInstance().getConference().setBreakoutRoomInListen(e.listen, e.breakoutMeetingId); } public function endAllBreakoutRooms(e:BreakoutRoomEvent):void { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as index 4c40e2923b283328d5c4e5bb9a5d439ffb2b194e..a7d3379ed7e6890ba6fb44370d6ac2ab1cc94f92 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as @@ -46,16 +46,7 @@ package org.bigbluebutton.modules.users.services import org.bigbluebutton.main.model.users.IMessageListener; import org.bigbluebutton.main.model.users.events.StreamStoppedEvent; import org.bigbluebutton.main.model.users.events.UsersConnectionEvent; - import org.bigbluebutton.modules.users.events.MeetingMutedEvent; - - import org.bigbluebutton.modules.present.events.CursorEvent; - import org.bigbluebutton.modules.present.events.NavigationEvent; - import org.bigbluebutton.modules.present.events.RemovePresentationEvent; - import org.bigbluebutton.modules.present.events.UploadEvent; - import org.bigbluebutton.modules.users.events.MeetingMutedEvent; - import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent; import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent; - import org.bigbluebutton.main.api.JSLog; import org.bigbluebutton.modules.users.events.MeetingMutedEvent; public class MessageReceiver implements IMessageListener @@ -156,8 +147,8 @@ package org.bigbluebutton.modules.users.services handleTimeRemainingUpdate(message); break; case "breakoutRoomsTimeRemainingUpdate": - handleBreakoutRoomsTimeRemainingUpdate(message); - break; + handleBreakoutRoomsTimeRemainingUpdate(message); + break; case "breakoutRoomStarted": handleBreakoutRoomStarted(message); break; @@ -639,8 +630,10 @@ package org.bigbluebutton.modules.users.services for each(var room : Object in map.rooms) { var breakoutRoom : BreakoutRoom = new BreakoutRoom(); - breakoutRoom.breakoutId = room.breakoutId; + breakoutRoom.meetingId = room.meetingId; + breakoutRoom.externalMeetingId = room.externalMeetingId; breakoutRoom.name = room.name; + breakoutRoom.sequence = room.sequence; UserManager.getInstance().getConference().addBreakoutRoom(breakoutRoom); } UserManager.getInstance().getConference().breakoutRoomsReady = map.roomsReady; @@ -650,13 +643,14 @@ package org.bigbluebutton.modules.users.services var map:Object = JSON.parse(msg.msg); var event : BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.BREAKOUT_JOIN_URL); event.joinURL = map.joinURL; - event.breakoutId = StringUtils.substringBetween(event.joinURL, "meetingID=", "&"); + var externalMeetingId : String = StringUtils.substringBetween(event.joinURL, "meetingID=", "&"); + event.breakoutMeetingSequence = UserManager.getInstance().getConference().getBreakoutRoomByExternalId(externalMeetingId).sequence; dispatcher.dispatchEvent(event); } private function handleUpdateBreakoutUsers(msg:Object):void{ var map:Object = JSON.parse(msg.msg); - UserManager.getInstance().getConference().updateBreakoutRoomUsers(map.breakoutId, map.users); + UserManager.getInstance().getConference().updateBreakoutRoomUsers(map.breakoutMeetingId, map.users); } private function handleTimeRemainingUpdate(msg:Object):void { @@ -676,14 +670,16 @@ package org.bigbluebutton.modules.users.services private function handleBreakoutRoomStarted(msg:Object):void{ var map:Object = JSON.parse(msg.msg); var breakoutRoom : BreakoutRoom = new BreakoutRoom(); - breakoutRoom.breakoutId = map.breakoutId; + breakoutRoom.meetingId = map.breakoutMeetingId; + breakoutRoom.externalMeetingId = map.externalMeetingId; breakoutRoom.name = map.name; + breakoutRoom.sequence = map.sequence; UserManager.getInstance().getConference().addBreakoutRoom(breakoutRoom); } private function handleBreakoutRoomClosed(msg:Object):void{ var map:Object = JSON.parse(msg.msg); - UserManager.getInstance().getConference().removeBreakoutRoom(map.breakoutId); + UserManager.getInstance().getConference().removeBreakoutRoom(map.breakoutMeetingId); } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as index a1ad1da52b03f14f6805970f3ce6def5e40662ac..9881a0fe0f5a13b9fe736c68e6ef0d8c80bddd2e 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as @@ -87,12 +87,13 @@ package org.bigbluebutton.modules.users.services ); } - public function createBreakoutRooms(meetingId:String, rooms:Array, durationInMinutes:int, record:Boolean):void { + public function createBreakoutRooms(meetingId:String, rooms:Array, durationInMinutes:int, record:Boolean, redirectOnJoin:Boolean):void { var message:Object = new Object(); message["meetingId"] = meetingId; message["rooms"] = rooms; message["durationInMinutes"] = durationInMinutes; message["record"] = record; + message["redirectOnJoin"] = redirectOnJoin; var jsonMsg:String = JSON.stringify(message); var _nc:ConnectionManager = BBB.initConnectionManager(); @@ -107,24 +108,21 @@ package org.bigbluebutton.modules.users.services ); } - public function requestBreakoutJoinUrl(meetingId:String, breakoutId:String, userId:String):void { + public function requestBreakoutJoinUrl(parentMeetingId:String, breakoutMeetingId:String, userId:String, redirect:Boolean):void { var message:Object = new Object(); - message["meetingId"] = meetingId; - message["breakoutId"] = breakoutId; + message["meetingId"] = parentMeetingId; + message["breakoutMeetingId"] = breakoutMeetingId; message["userId"] = userId; + message["redirect"] = redirect; var jsonMsg:String = JSON.stringify(message); var _nc:ConnectionManager = BBB.initConnectionManager(); - _nc.sendMessage("breakoutroom.requestBreakoutJoinUrl", function(result:String):void - { + _nc.sendMessage("breakoutroom.requestBreakoutJoinUrl", function(result:String):void { // On successful result - }, function(status:String):void - { // status - On error occurred + }, function(status:String):void { // status - On error occurred LOGGER.error(status); - }, - jsonMsg - ); + }, jsonMsg); } public function listenInOnBreakout(meetingId:String, targetMeetingId:String, userId:String):void { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml index 13075f7fcda0bb5e9e0a9e219df4d66bd0605e85..8a3b007d526f0f183968b6485aba83c9241d3f0a 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml @@ -65,15 +65,15 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. var event:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.CREATE_BREAKOUT_ROOMS); // event.meetingId is filled in the event handler for BreakoutRoomEvent in UserService class event.rooms = new Array(); + var parentMeetingName:String = UserManager.getInstance().getConference().meetingName; + var roomResource:String = ResourceUtil.getInstance().getString('bbb.users.breakout.room'); for (var i:int = 0; i < (roomsCombo.selectedIndex + 2); i++) { var users:Array = BreakoutList(roomsContainer.getChildAt(i)).users.source; totalUsers += users.length; var room:Object = new Object(); room.users = new Array(); - room.name = - UserManager.getInstance().getConference().meetingName + " (" + - ResourceUtil.getInstance().getString('bbb.users.breakout.room') - + " - " + (i + 1).toString() + ")"; + room.sequence = i + 1; + room.name = parentMeetingName + " (" + roomResource + " - " + room.sequence.toString() + ")"; for (var j:int = 0; j < users.length; j++) { room.users.push(users[j].userID); } @@ -105,7 +105,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. usersInvited ||= true; var event:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.REQUEST_BREAKOUT_JOIN_URL); event.userId = user.userID; - event.breakoutId = UserManager.getInstance().getConference().internalMeetingID + "-" + (i + 1); + event.breakoutMeetingId = UserManager.getInstance().getConference().getBreakoutRoomBySequence(i + 1).externalMeetingId; dispatcher.dispatchEvent(event); } } @@ -294,8 +294,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. </mx:HBox> <mx:HBox id="recordBox" width="100%" paddingTop="12"> - <mx:Label text="{ResourceUtil.getInstance().getString('bbb.users.breakout.record')}" visible="false" /> - <mx:CheckBox id="recordCheckbox" visible="false" + <mx:Label text="{ResourceUtil.getInstance().getString('bbb.users.breakout.record')}"/> + <mx:CheckBox id="recordCheckbox" accessibilityName="{ResourceUtil.getInstance().getString('bbb.users.breakout.recordCheckbox.accessibilityName')}"/> </mx:HBox> <mx:Tile id="roomsContainer" styleName="roomsContainer" width="100%" height="100%"/> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml index bf5ccf439036e4b9227d257f0dd6d5299aefeb56..65f5cfdb93e8f91e3ccdcf12d7f2e0294e768b64 100644 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml @@ -37,14 +37,14 @@ protected function listenToBreakoutRoom(event:MouseEvent):void { var e:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.LISTEN_IN); - e.breakoutId = data.breakoutId as String; + e.breakoutMeetingId = data.meetingId as String; e.listen = listenBtn.selected; globalDispatch.dispatchEvent(e); } protected function requestBreakoutJoinUrl(event:MouseEvent):void { var e:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.REQUEST_BREAKOUT_JOIN_URL); - e.breakoutId = data.breakoutId as String; + e.breakoutMeetingId = data.externalMeetingId as String; e.userId = UserManager.getInstance().getConference().getMyUserId(); globalDispatch.dispatchEvent(e); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml index 728d2016de8161c7cf5e3b740cec122714bad3fa..81162c71cb5885ebfd4bf8e29c4422d2e20f6860 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml @@ -55,7 +55,6 @@ import mx.events.MenuEvent; import mx.managers.PopUpManager; - import org.as3commons.lang.StringUtils; import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.getClassLogger; import org.bigbluebutton.common.IBbbModuleWindow; @@ -343,7 +342,7 @@ // We display only one alert removeJoinAlert(); joinAlert = Alert.show( - ResourceUtil.getInstance().getString('bbb.users.breakout.openJoinURL', [StringUtils.substringAfterLast(event.breakoutId, "-")]), + ResourceUtil.getInstance().getString('bbb.users.breakout.openJoinURL', [event.breakoutMeetingSequence]), ResourceUtil.getInstance().getString('bbb.users.breakout.confirm'), Alert.YES | Alert.NO, null, @@ -514,7 +513,7 @@ var br:BreakoutRoom = UserManager.getInstance().getConference().getBreakoutRoomInListen(); if (br != null) { var e:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.LISTEN_IN); - e.breakoutId = br.breakoutId; + e.breakoutMeetingId = br.meetingId; e.listen = false; dispatcher.dispatchEvent(e); } @@ -595,7 +594,7 @@ } private function breakoutRoomNameLabelFunction(item:Object, column:DataGridColumn) : String { - return ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room') + " " + StringUtils.substringAfterLast(item.breakoutId, "-"); + return ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room') + " " + item.sequence; } ]]> </mx:Script> @@ -633,7 +632,7 @@ <mx:columns> <mx:DataGridColumn labelFunction="breakoutRoomNameLabelFunction" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room')}" /> <mx:DataGridColumn dataField="numberOfUsers" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.users')}"/> - <mx:DataGridColumn dataField="breakoutId" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.action')}" + <mx:DataGridColumn dataField="meetingId" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.action')}" visible="{amIModerator}" itemRenderer="org.bigbluebutton.modules.users.views.RoomActionsRenderer"/> </mx:columns> diff --git a/bigbluebutton-config/bin/bbb-record b/bigbluebutton-config/bin/bbb-record index 6adbe521660c1729d0ef9a5d31467790a9cdb292..608707dbfeadfb9172c1dc9b84e471b0a79e2c9d 100755 --- a/bigbluebutton-config/bin/bbb-record +++ b/bigbluebutton-config/bin/bbb-record @@ -33,7 +33,7 @@ # 2013-04-05 GUG Description is optional in bbb-record --watch # 2013-04-05 GUG Map internal meeting id with external meeting id # 2016-07-02 FFD Updates for 1.1 -# 2016-09-27 GTR Stricter recording directories names detection to cover breakout rooms recordings +# 2016-10-17 GTR Stricter rule for detection of recording directories names #set -e #set -x @@ -408,7 +408,7 @@ if [ $DELETEALL ]; then rm -f /var/bigbluebutton/screenshare/*.flv rm -f /var/freeswitch/meetings/*.wav - for meeting in $(ls /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$"); do + for meeting in $(ls /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$"); do echo "deleting: $meeting" rm -rf /var/bigbluebutton/$meeting done @@ -448,10 +448,10 @@ if [ -z $HEAD ]; then fi tmp_file=$(mktemp) -ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$" | head -n $HEAD > $tmp_file -ls -t /var/bigbluebutton/recording/raw | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$" | head -n $HEAD >> $tmp_file +ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$" | head -n $HEAD > $tmp_file +ls -t /var/bigbluebutton/recording/raw | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$" | head -n $HEAD >> $tmp_file -#for meeting in $(ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$" | head -n $HEAD); do +#for meeting in $(ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$" | head -n $HEAD); do for meeting in $(cat $tmp_file | sort -t - -k 2 -r| uniq); do echo -n "$meeting" timestamp=$(echo $meeting | sed s/.*-//g) diff --git a/bigbluebutton-config/slides/blank-presentation.pdf b/bigbluebutton-config/slides/blank-presentation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ebf6257403b88d9f0eeeb1dfcb096f0c9fc52a9d Binary files /dev/null and b/bigbluebutton-config/slides/blank-presentation.pdf differ diff --git a/bigbluebutton-web/build.gradle b/bigbluebutton-web/build.gradle index 67fb9d41422537d2123fe0effebe5320d40b0d70..588866d978f8b0817f50e50bda2e38bb5969a83d 100755 --- a/bigbluebutton-web/build.gradle +++ b/bigbluebutton-web/build.gradle @@ -19,6 +19,7 @@ dependencies { compile 'commons-lang:commons-lang:2.5' compile 'commons-io:commons-io:2.4' + compile 'commons-codec:commons-codec:1.10' compile 'com.google.code.gson:gson:1.7.1' compile 'commons-httpclient:commons-httpclient:3.1' compile 'com.zaxxer:nuprocess:1.1.0' diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties index 10e12539b3b003d3f8f3552aa567b6afe0e4024f..1c9d5a130a8612393d3c55edf73eeaaef49bbfae 100755 --- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties +++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties @@ -1,220 +1,221 @@ -# -# 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/>. -# - -# -# These are the default properites for BigBlueButton Web application - -# Default loglevel. -appLogLevel=DEBUG - -#---------------------------------------------------- -# Directory where BigBlueButton stores uploaded slides -presentationDir=/var/bigbluebutton - -#---------------------------------------------------- -# Directory where SWFTOOLS (pdf2swf, jpeg2swf, png2swf) are located -swfToolsDir=/usr/bin - -#---------------------------------------------------- -# Directory where ImageMagick's convert executable is located -imageMagickDir=/usr/bin - -#---------------------------------------------------- -# Use fullpath to ghostscript executable since the exec names are different -# for each platform. -ghostScriptExec=/usr/bin/gs - -#---------------------------------------------------- -# Fonts directory passed into PDF2SWF to support highlighting of texts -# in the SWF slides. -fontsDir=/usr/share/fonts - -#---------------------------------------------------- -# This is a workaround for a problem converting PDF files, referenced at -# http://groups.google.com/group/comp.lang.postscript/browse_thread/thread/c2e264ca76534ce0?pli=1 -noPdfMarkWorkaround=/etc/bigbluebutton/nopdfmark.ps - -#---------------------------------------------------- -# These will be copied in cases where the conversion process -# fails to generate a slide from the uploaded presentation -BLANK_SLIDE=/var/bigbluebutton/blank/blank-slide.swf -BLANK_THUMBNAIL=/var/bigbluebutton/blank/blank-thumb.png - -#---------------------------------------------------- -# Number of minutes the conversion should take. If it takes -# more than this time, cancel the conversion process. -maxConversionTime=5 - -#---------------------------------------------------- -# Maximum number of pages allowed for an uploaded presentation (default 100). -maxNumPages=200 - -#---------------------------------------------------- -# Maximum swf file size for load to the client (default 500000). -MAX_SWF_FILE_SIZE=500000 - -#---------------------------------------------------- -# Maximum allowed number of place object tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000) -placementsThreshold=8000 - -# Maximum allowed number of bitmap images in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000) -imageTagThreshold=8000 - -# Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 2500) -defineTextThreshold=2500 - -#------------------------------------ -# Number of threads in the pool to do the presentation conversion. -#------------------------------------ -numConversionThreads=2 - -#---------------------------------------------------- -# Additional conversion of the presentation slides to SVG -# to be used in the HTML5 client -svgImagesRequired=false - -# Default number of digits for voice conference users joining through the PSTN. -defaultNumDigitsForTelVoice=5 - -#---------------------------------------------------- -# Default dial access number -defaultDialAccessNumber=613-555-1234 - -#---------------------------------------------------- -# Default welcome message to display when the participant joins the web -# conference. This is only used for the old scheduling which will be -# removed in the future. Use the API to create a conference. -defaultWelcomeMessage=<br>Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the headset icon (upper-left hand corner). Use a headset to avoid causing background noise for others.<br> -defaultWelcomeMessageFooter=This server is running <a href="http://docs.bigbluebutton.org/" target="_blank"><u>BigBlueButton</u></a>. - -# Default maximum number of users a meeting can have. -# Current default is 0 (meeting doesn't have a user limit). -defaultMaxUsers=0 - -# Default duration of the meeting in minutes. -# Current default is 0 (meeting doesn't end). -defaultMeetingDuration=0 - -# Remove the meeting from memory when the end API is called. -# This allows 3rd-party apps to recycle the meeting right-away -# instead of waiting for the meeting to expire (see below). -removeMeetingWhenEnded=true - -# The number of minutes before the system removes the meeting from memory. -defaultMeetingExpireDuration=1 - -# The number of minutes the system waits when a meeting is created and when -# a user joins. If after this period, a user hasn't joined, the meeting is -# removed from memory. -defaultMeetingCreateJoinDuration=5 - -# Disable recording by default. -# true - don't record even if record param in the api call is set to record -# false - when record param is passed from api, override this default -disableRecordingDefault=false - -# Start recording when first user joins the meeting. -# For backward compatibility with 0.81 where whole meeting -# is recorded. -autoStartRecording=false - -# Allow the user to start/stop recording. -allowStartStopRecording=true - -#---------------------------------------------------- -# This URL is where the BBB client is accessible. When a user sucessfully -# enters a name and password, she is redirected here to load the client. -bigbluebutton.web.serverURL=http://192.168.23.44 - - -#---------------------------------------------------- -# Assign URL where the logged-out participant will be redirected after sign-out. -# If "default", it returns to bigbluebutton.web.serverURL -bigbluebutton.web.logoutURL=default - -# The url of the BigBlueButton client. User's will be redirected here when -# successfully joining the meeting. -defaultClientUrl=${bigbluebutton.web.serverURL}/client/BigBlueButton.html -#defaultClientUrl=http://192.168.0.235/3rd-party.html - -# The default avatar image to display if nothing is passed on the JOIN API (avatarURL) -# call. This avatar is displayed if the user isn't sharing the webcam and -# the option (displayAvatar) is enabled in config.xml -defaultAvatarURL=${bigbluebutton.web.serverURL}/client/avatar.png - -# The URL of the default configuration -defaultConfigURL=${bigbluebutton.web.serverURL}/client/conf/config.xml - -apiVersion=1.0 - -# Salt which is used by 3rd-party apps to authenticate api calls -securitySalt=a820d30da2db356124fce5bd5d8054b4 - -# Directory where we drop the <meeting-id-recorded>.done file -recordStatusDir=/var/bigbluebutton/recording/status/recorded - -redisHost=127.0.0.1 -redisPort=6379 - -# The directory where the published/unpublised recordings are located. This is for -# the get recording* api calls -publishedDir=/var/bigbluebutton/published -unpublishedDir=/var/bigbluebutton/unpublished - -# The directory where the pre-built configs are stored -configDir=/var/bigbluebutton/configs - -# If the API is enabled. -serviceEnabled = true - -# Test voiceBridge number -testVoiceBridge=99999 -testConferenceMock=conference-mock-default - -#------------------------------------------------------ -# These properties are used to test the conversion process. -# Conference name folder in ${presentationDir} (see above) -beans.presentationService.testConferenceMock=${testConferenceMock} - -# Conference room folder in ${presentationDir}/${testConferenceMock} -beans.presentationService.testRoomMock=conference-mock-default -# Uploaded presentation name -beans.presentationService.testPresentationName=appkonference -# Uploaded presentation file -beans.presentationService.testUploadedPresentation=appkonference.txt -# Default Uploaded presentation file -beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf - -presentationBaseURL=${bigbluebutton.web.serverURL}/bigbluebutton/presentation - -#---------------------------------------------------- -# The URL where the presentations will be loaded from. -#---------------------------------------------------- -beans.presentationService.presentationBaseUrl=${presentationBaseURL} -#---------------------------------------------------- -# Inject values into grails service beans -beans.presentationService.presentationDir=${presentationDir} - -#---------------------------------------------------- -# Specify which IPs can do cross domain requests -accessControlAllowOrigin=${bigbluebutton.web.serverURL} - -#---------------------------------------------------- -# The lapsus of seconds for polling the BBB Server in order to check if it's down. -# After 5 tries if there isn't response, it will be declared down -checkBBBServerEvery=10 +# +# 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/>. +# + +# +# These are the default properites for BigBlueButton Web application + +# Default loglevel. +appLogLevel=DEBUG + +#---------------------------------------------------- +# Directory where BigBlueButton stores uploaded slides +presentationDir=/var/bigbluebutton + +#---------------------------------------------------- +# Directory where SWFTOOLS (pdf2swf, jpeg2swf, png2swf) are located +swfToolsDir=/usr/bin + +#---------------------------------------------------- +# Directory where ImageMagick's convert executable is located +imageMagickDir=/usr/bin + +#---------------------------------------------------- +# Use fullpath to ghostscript executable since the exec names are different +# for each platform. +ghostScriptExec=/usr/bin/gs + +#---------------------------------------------------- +# Fonts directory passed into PDF2SWF to support highlighting of texts +# in the SWF slides. +fontsDir=/usr/share/fonts + +#---------------------------------------------------- +# This is a workaround for a problem converting PDF files, referenced at +# http://groups.google.com/group/comp.lang.postscript/browse_thread/thread/c2e264ca76534ce0?pli=1 +noPdfMarkWorkaround=/etc/bigbluebutton/nopdfmark.ps + +#---------------------------------------------------- +# These will be copied in cases where the conversion process +# fails to generate a slide from the uploaded presentation +BLANK_SLIDE=/var/bigbluebutton/blank/blank-slide.swf +BLANK_PRESENTATION=/var/bigbluebutton/blank/blank-presentation.pdf +BLANK_THUMBNAIL=/var/bigbluebutton/blank/blank-thumb.png + +#---------------------------------------------------- +# Number of minutes the conversion should take. If it takes +# more than this time, cancel the conversion process. +maxConversionTime=5 + +#---------------------------------------------------- +# Maximum number of pages allowed for an uploaded presentation (default 100). +maxNumPages=200 + +#---------------------------------------------------- +# Maximum swf file size for load to the client (default 500000). +MAX_SWF_FILE_SIZE=500000 + +#---------------------------------------------------- +# Maximum allowed number of place object tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000) +placementsThreshold=8000 + +# Maximum allowed number of bitmap images in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000) +imageTagThreshold=8000 + +# Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 2500) +defineTextThreshold=2500 + +#------------------------------------ +# Number of threads in the pool to do the presentation conversion. +#------------------------------------ +numConversionThreads=2 + +#---------------------------------------------------- +# Additional conversion of the presentation slides to SVG +# to be used in the HTML5 client +svgImagesRequired=false + +# Default number of digits for voice conference users joining through the PSTN. +defaultNumDigitsForTelVoice=5 + +#---------------------------------------------------- +# Default dial access number +defaultDialAccessNumber=613-555-1234 + +#---------------------------------------------------- +# Default welcome message to display when the participant joins the web +# conference. This is only used for the old scheduling which will be +# removed in the future. Use the API to create a conference. +defaultWelcomeMessage=<br>Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the headset icon (upper-left hand corner). Use a headset to avoid causing background noise for others.<br> +defaultWelcomeMessageFooter=This server is running <a href="http://docs.bigbluebutton.org/" target="_blank"><u>BigBlueButton</u></a>. + +# Default maximum number of users a meeting can have. +# Current default is 0 (meeting doesn't have a user limit). +defaultMaxUsers=0 + +# Default duration of the meeting in minutes. +# Current default is 0 (meeting doesn't end). +defaultMeetingDuration=0 + +# Remove the meeting from memory when the end API is called. +# This allows 3rd-party apps to recycle the meeting right-away +# instead of waiting for the meeting to expire (see below). +removeMeetingWhenEnded=true + +# The number of minutes before the system removes the meeting from memory. +defaultMeetingExpireDuration=1 + +# The number of minutes the system waits when a meeting is created and when +# a user joins. If after this period, a user hasn't joined, the meeting is +# removed from memory. +defaultMeetingCreateJoinDuration=5 + +# Disable recording by default. +# true - don't record even if record param in the api call is set to record +# false - when record param is passed from api, override this default +disableRecordingDefault=false + +# Start recording when first user joins the meeting. +# For backward compatibility with 0.81 where whole meeting +# is recorded. +autoStartRecording=false + +# Allow the user to start/stop recording. +allowStartStopRecording=true + +#---------------------------------------------------- +# This URL is where the BBB client is accessible. When a user sucessfully +# enters a name and password, she is redirected here to load the client. +bigbluebutton.web.serverURL=http://192.168.23.44 + + +#---------------------------------------------------- +# Assign URL where the logged-out participant will be redirected after sign-out. +# If "default", it returns to bigbluebutton.web.serverURL +bigbluebutton.web.logoutURL=default + +# The url of the BigBlueButton client. User's will be redirected here when +# successfully joining the meeting. +defaultClientUrl=${bigbluebutton.web.serverURL}/client/BigBlueButton.html +#defaultClientUrl=http://192.168.0.235/3rd-party.html + +# The default avatar image to display if nothing is passed on the JOIN API (avatarURL) +# call. This avatar is displayed if the user isn't sharing the webcam and +# the option (displayAvatar) is enabled in config.xml +defaultAvatarURL=${bigbluebutton.web.serverURL}/client/avatar.png + +# The URL of the default configuration +defaultConfigURL=${bigbluebutton.web.serverURL}/client/conf/config.xml + +apiVersion=1.0 + +# Salt which is used by 3rd-party apps to authenticate api calls +securitySalt=a820d30da2db356124fce5bd5d8054b4 + +# Directory where we drop the <meeting-id-recorded>.done file +recordStatusDir=/var/bigbluebutton/recording/status/recorded + +redisHost=127.0.0.1 +redisPort=6379 + +# The directory where the published/unpublised recordings are located. This is for +# the get recording* api calls +publishedDir=/var/bigbluebutton/published +unpublishedDir=/var/bigbluebutton/unpublished + +# The directory where the pre-built configs are stored +configDir=/var/bigbluebutton/configs + +# If the API is enabled. +serviceEnabled = true + +# Test voiceBridge number +testVoiceBridge=99999 +testConferenceMock=conference-mock-default + +#------------------------------------------------------ +# These properties are used to test the conversion process. +# Conference name folder in ${presentationDir} (see above) +beans.presentationService.testConferenceMock=${testConferenceMock} + +# Conference room folder in ${presentationDir}/${testConferenceMock} +beans.presentationService.testRoomMock=conference-mock-default +# Uploaded presentation name +beans.presentationService.testPresentationName=appkonference +# Uploaded presentation file +beans.presentationService.testUploadedPresentation=appkonference.txt +# Default Uploaded presentation file +beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf + +presentationBaseURL=${bigbluebutton.web.serverURL}/bigbluebutton/presentation + +#---------------------------------------------------- +# The URL where the presentations will be loaded from. +#---------------------------------------------------- +beans.presentationService.presentationBaseUrl=${presentationBaseURL} +#---------------------------------------------------- +# Inject values into grails service beans +beans.presentationService.presentationDir=${presentationDir} + +#---------------------------------------------------- +# Specify which IPs can do cross domain requests +accessControlAllowOrigin=${bigbluebutton.web.serverURL} + +#---------------------------------------------------- +# The lapsus of seconds for polling the BBB Server in order to check if it's down. +# After 5 tries if there isn't response, it will be declared down +checkBBBServerEvery=10 diff --git a/bigbluebutton-web/grails-app/conf/spring/resources.xml b/bigbluebutton-web/grails-app/conf/spring/resources.xml index 48cc70b20a40d2b2db63b7e17d4d797d98462318..4c8319a9cd832726b83ae049900f790c06e0771c 100755 --- a/bigbluebutton-web/grails-app/conf/spring/resources.xml +++ b/bigbluebutton-web/grails-app/conf/spring/resources.xml @@ -61,6 +61,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <property name="presentationBaseURL" value="${presentationBaseURL}"/> <property name="pageExtractor" ref="pageExtractor"/> <property name="documentConversionService" ref="documentConversionService"/> + <property name="blankPresentation" value="${BLANK_PRESENTATION}"/> </bean> <bean id="recordingService" class="org.bigbluebutton.api.RecordingService" > diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index ef9d2b8918d83e618000b531b3ce20d992f18141..05fde7f168dfc3ddff9ee424a42b945f996c867f 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -286,19 +286,9 @@ class ApiController { return } - Boolean isBreakoutRoom = false - if(!StringUtils.isEmpty(params.isBreakout)) { - isBreakoutRoom = new Boolean(StringUtils.strip(params.isBreakout)) - } - // Everything is good so far. Translate the external meeting id to an internal meeting id. If // we can't find the meeting, complain. String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId); - if (isBreakoutRoom) { - // This is a join request for a breakout room. Use the passed meetingId to find the meeting. - internalMeetingId = externalMeetingId - log.info("Join request for breakout room " + internalMeetingId) - } log.info("Retrieving meeting ${internalMeetingId}") Meeting meeting = meetingService.getMeeting(internalMeetingId); @@ -869,6 +859,10 @@ class ApiController { meeting { meetingID() { mkp.yield(m.getExternalId()) } internalMeetingID() { mkp.yield(m.getInternalId()) } + if (m.isBreakout()) { + parentMeetingID() { mkp.yield(m.getParentMeetingId()) } + sequence(m.getSequence()) + } isBreakout() { mkp.yield(m.isBreakout()) } meetingName() { mkp.yield(m.getName()) } createTime(m.getCreateTime()) @@ -2136,6 +2130,10 @@ class ApiController { isBreakout() { mkp.yield(meeting.isBreakout()) } meetingID() { mkp.yield(meeting.getExternalId()) } internalMeetingID(meeting.getInternalId()) + if (m.isBreakout()) { + parentMeetingID() { mkp.yield(meeting.getParentMeetingId()) } + sequence(meeting.getSequence()) + } createTime(meeting.getCreateTime()) createDate(formatPrettyDate(meeting.getCreateTime())) voiceBridge() { mkp.yield(meeting.getTelVoice()) } @@ -2196,6 +2194,7 @@ class ApiController { returncode(RESP_CODE_SUCCESS) meetingID() { mkp.yield(meeting.getExternalId()) } internalMeetingID() { mkp.yield(meeting.getInternalId()) } + parentMeetingID() { mkp.yield(meeting.getParentMeetingId()) } attendeePW() { mkp.yield(meeting.getViewerPassword()) } moderatorPW() { mkp.yield(meeting.getModeratorPassword()) } createTime(meeting.getCreateTime()) diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java index c06f635980917d19fcf9d7b3c0b916817c0a1d54..f4d59fb037174815e96acd7d050b4021e4645107 100644 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java @@ -19,9 +19,6 @@ package org.bigbluebutton.api; -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; import java.util.HashMap; import java.util.Map; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java index afbb19b22aa8b0c56615e962f1ce09e75c7ea8c0..1f3249cd3e2389d7af96bed40472248d26f1626a 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java @@ -301,15 +301,27 @@ public class MeetingService implements MessageListener { // TODO: Need a better way to store these values for recordings metadata.put("meetingId", m.getExternalId()); metadata.put("meetingName", m.getName()); + metadata.put("isBreakout", m.isBreakout().toString()); - messagingService.recordMeetingInfo(m.getInternalId(), metadata); + Map<String, String> breakoutMetadata = new TreeMap<String, String>(); + if (m.isBreakout() != null){ + breakoutMetadata.put("sequence", m.getSequence().toString()); + breakoutMetadata.put("parentMeetingId", m.getParentMeetingId()); + } + + messagingService.recordMeetingInfo(m.getInternalId(), metadata, breakoutMetadata); } Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", m.getInternalId()); logData.put("externalMeetingId", m.getExternalId()); + if (m.isBreakout() != null){ + logData.put("sequence", m.getSequence()); + logData.put("parentMeetingId", m.getParentMeetingId()); + } logData.put("name", m.getName()); logData.put("duration", m.getDuration()); + logData.put("isBreakout", m.isBreakout()); logData.put("record", m.isRecord()); logData.put("event", "create_meeting"); logData.put("description", "Create meeting."); @@ -320,11 +332,11 @@ public class MeetingService implements MessageListener { log.info("Create meeting: data={}", logStr); messagingService.createMeeting(m.getInternalId(), m.getExternalId(), - m.getName(), m.isRecord(), m.getTelVoice(), m.getDuration(), - m.getAutoStartRecording(), m.getAllowStartStopRecording(), - m.getModeratorPassword(), m.getViewerPassword(), - m.getCreateTime(), formatPrettyDate(m.getCreateTime()), - m.isBreakout()); + m.getParentMeetingId(), m.getName(), m.isRecord(), + m.getTelVoice(), m.getDuration(), m.getAutoStartRecording(), + m.getAllowStartStopRecording(), m.getModeratorPassword(), + m.getViewerPassword(), m.getCreateTime(), + formatPrettyDate(m.getCreateTime()), m.isBreakout(), m.getSequence()); } private String formatPrettyDate(Long timestamp) { @@ -359,15 +371,9 @@ public class MeetingService implements MessageListener { public Meeting getMeeting(String meetingId) { if (meetingId == null) return null; - int dashes = meetingId.split("-", -1).length - 1; for (String key : meetings.keySet()) { - int keyDashes = key.split("-", -1).length - 1; - if (dashes == 2 - && key.equals(meetingId) - || (dashes < 2 && keyDashes < 2 && key - .startsWith(meetingId))) { + if (key.startsWith(meetingId)) return (Meeting) meetings.get(key); - } } return null; @@ -515,20 +521,21 @@ public class MeetingService implements MessageListener { } private void processCreateBreakoutRoom(CreateBreakoutRoom message) { - Meeting parentMeeting = getMeeting(message.parentId); + Meeting parentMeeting = getMeeting(message.parentMeetingId); if (parentMeeting != null) { Map<String, String> params = new HashMap<String, String>(); params.put("name", message.name); - params.put("breakoutId", message.breakoutId); - params.put("meetingID", message.parentId); + params.put("meetingID", message.meetingId); + params.put("parentMeetingID", message.parentMeetingId); params.put("isBreakout", "true"); + params.put("sequence", message.sequence.toString()); params.put("attendeePW", message.viewerPassword); params.put("moderatorPW", message.moderatorPassword); params.put("voiceBridge", message.voiceConfId); params.put("duration", message.durationInMinutes.toString()); params.put("record", message.record.toString()); - params.put("welcome", getMeeting(message.parentId) + params.put("welcome", getMeeting(message.parentMeetingId) .getWelcomeMessageTemplate()); Map<String, String> parentMeetingMetadata = parentMeeting @@ -545,17 +552,18 @@ public class MeetingService implements MessageListener { handleCreateMeeting(breakout); - presDownloadService.extractPage(message.parentId, + presDownloadService.extractPage(message.parentMeetingId, message.sourcePresentationId, message.sourcePresentationSlide, breakout.getInternalId()); } else { - log.error("Failed to create breakout room " + message.breakoutId - + ".Reason: Parent meeting not found."); + log.error( + "Failed to create breakout room {}.Reason: Parent meeting {} not found.", + message.meetingId, message.parentMeetingId); } } private void processEndBreakoutRoom(EndBreakoutRoom message) { - processEndMeeting(new EndMeeting(message.breakoutId)); + processEndMeeting(new EndMeeting(message.breakoutMeetingId)); } private void processEndMeeting(EndMeeting message) { @@ -591,6 +599,9 @@ public class MeetingService implements MessageListener { Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", m.getInternalId()); logData.put("externalMeetingId", m.getExternalId()); + if (m.isBreakout()) { + logData.put("parentMeetingId", m.getParentMeetingId()); + } logData.put("name", m.getName()); logData.put("duration", m.getDuration()); logData.put("record", m.isRecord()); @@ -601,11 +612,14 @@ public class MeetingService implements MessageListener { Gson gson = new Gson(); String logStr = gson.toJson(logData); - log.info("Meeting restarted: data={}", logStr); + log.info("Meeting started: data={}", logStr); } else { Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", m.getInternalId()); logData.put("externalMeetingId", m.getExternalId()); + if (m.isBreakout()) { + logData.put("parentMeetingId", m.getParentMeetingId()); + } logData.put("name", m.getName()); logData.put("duration", m.getDuration()); logData.put("record", m.isRecord()); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java index 37517ec569d345f64babe564b795a1786e61fe63..be29faa84fef1c75d9840b5546172c82ddb7e683 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java @@ -288,84 +288,108 @@ public class ParamsProcessorUtil { return metas; } - public Meeting processCreateParams(Map<String, String> params) { - String meetingName = params.get("name"); - if(meetingName == null){ - meetingName = ""; - } - String externalMeetingId = params.get("meetingID"); - - String viewerPass = processPassword(params.get("attendeePW")); - String modPass = processPassword(params.get("moderatorPW")); - - // Get the digits for voice conference for users joining through the phone. - // If none is provided, generate one. - String telVoice = processTelVoice(params.get("voiceBridge")); - - // Get the voice conference digits/chars for users joing through VOIP on the client. - // If none is provided, make it the same as the telVoice. If one has been provided, - // we expect that the users will be joined in the same voice conference. - String webVoice = params.get("webVoice"); - if (StringUtils.isEmpty(webVoice)) { - webVoice = telVoice; - } - - // Get all the other relevant parameters and generate defaults if none has been provided. - String dialNumber = processDialNumber(params.get("dialNumber")); - String logoutUrl = processLogoutUrl(params.get("logoutURL")); - boolean record = processRecordMeeting(params.get("record")); - int maxUsers = processMaxUser(params.get("maxParticipants")); - int meetingDuration = processMeetingDuration(params.get("duration")); - + public Meeting processCreateParams(Map<String, String> params) { + String meetingName = params.get("name"); + if (meetingName == null) { + meetingName = ""; + } + String externalMeetingId = params.get("meetingID"); + + String viewerPass = processPassword(params.get("attendeePW")); + String modPass = processPassword(params.get("moderatorPW")); + + // Get the digits for voice conference for users joining through the + // phone. + // If none is provided, generate one. + String telVoice = processTelVoice(params.get("voiceBridge")); + + // Get the voice conference digits/chars for users joing through VOIP on + // the client. + // If none is provided, make it the same as the telVoice. If one has + // been provided, + // we expect that the users will be joined in the same voice conference. + String webVoice = params.get("webVoice"); + if (StringUtils.isEmpty(webVoice)) { + webVoice = telVoice; + } + + // Get all the other relevant parameters and generate defaults if none + // has been provided. + String dialNumber = processDialNumber(params.get("dialNumber")); + String logoutUrl = processLogoutUrl(params.get("logoutURL")); + boolean record = processRecordMeeting(params.get("record")); + int maxUsers = processMaxUser(params.get("maxParticipants")); + int meetingDuration = processMeetingDuration(params.get("duration")); + // set is breakout room property boolean isBreakout = false; if (!StringUtils.isEmpty(params.get("isBreakout"))) { isBreakout = new Boolean(params.get("isBreakout")); } - String welcomeMessageTemplate = processWelcomeMessage(params.get("welcome"), isBreakout); - String welcomeMessage = substituteKeywords(welcomeMessageTemplate, dialNumber, telVoice, meetingName); - - String internalMeetingId = convertToInternalMeetingId(externalMeetingId); - - // Check if this is a test meeting. NOTE: This should not belong here. Extract this out. - if (isTestMeeting(telVoice)) { - internalMeetingId = getIntMeetingIdForTestMeeting(telVoice); - } - - boolean autoStartRec = autoStartRecording; - if (!StringUtils.isEmpty(params.get("autoStartRecording"))) { - try { - autoStartRec = Boolean.parseBoolean(params.get("autoStartRecording")); - } catch(Exception ex){ - log.warn("Invalid param [autoStartRecording] for meeting=[" + internalMeetingId + "]"); - } - } + String welcomeMessageTemplate = processWelcomeMessage( + params.get("welcome"), isBreakout); + String welcomeMessage = substituteKeywords(welcomeMessageTemplate, + dialNumber, telVoice, meetingName); - boolean allowStartStoptRec = allowStartStopRecording; - if (!StringUtils.isEmpty(params.get("allowStartStopRecording"))) { - try { - allowStartStoptRec = Boolean.parseBoolean(params.get("allowStartStopRecording")); - } catch(Exception ex){ - log.warn("Invalid param [allowStartStopRecording] for meeting=[" + internalMeetingId + "]"); - } - } - - // Collect metadata for this meeting that the third-party app wants to store if meeting is recorded. - Map<String, String> meetingInfo = new HashMap<String, String>(); - meetingInfo = processMetaParam(params); - - // Create a unique internal id by appending the current time. This way, the 3rd-party - // app can reuse the external meeting id. - long createTime = System.currentTimeMillis(); - internalMeetingId = internalMeetingId + '-' + new Long(createTime).toString(); + String internalMeetingId = convertToInternalMeetingId(externalMeetingId); + + // Check if this is a test meeting. NOTE: This should not belong here. + // Extract this out. + if (isTestMeeting(telVoice)) { + internalMeetingId = getIntMeetingIdForTestMeeting(telVoice); + } + + boolean autoStartRec = autoStartRecording; + if (!StringUtils.isEmpty(params.get("autoStartRecording"))) { + try { + autoStartRec = Boolean.parseBoolean(params + .get("autoStartRecording")); + } catch (Exception ex) { + log.warn("Invalid param [autoStartRecording] for meeting=[{}]", + internalMeetingId); + } + } + + boolean allowStartStoptRec = allowStartStopRecording; + if (!StringUtils.isEmpty(params.get("allowStartStopRecording"))) { + try { + allowStartStoptRec = Boolean.parseBoolean(params + .get("allowStartStopRecording")); + } catch (Exception ex) { + log.warn( + "Invalid param [allowStartStopRecording] for meeting=[{}]", + internalMeetingId); + } + } + + // Collect metadata for this meeting that the third-party app wants to + // store if meeting is recorded. + Map<String, String> meetingInfo = new HashMap<String, String>(); + meetingInfo = processMetaParam(params); + + // Create a unique internal id by appending the current time. This way, + // the 3rd-party + // app can reuse the external meeting id. + long createTime = System.currentTimeMillis(); + internalMeetingId = internalMeetingId.concat("-").concat( + new Long(createTime).toString()); // If this create meeting request is for a breakout room, we just used - // the passed in breakoutId as the internal meetingId so we can - // correlate - // the breakout meeting with it's parent meeting. + // we need to generate a unique internal and external id and keep + // tracks of the parent meeting id + String parentMeetingId = new String(); if (isBreakout) { - internalMeetingId = params.get("breakoutId"); + internalMeetingId = params.get("meetingID"); + parentMeetingId = params.get("parentMeetingID"); + // We rebuild the the external meeting using the has of the parent + // meeting, the shared timestamp and the sequence number + String timeStamp = StringUtils.substringAfter(internalMeetingId, + "-"); + String externalHash = DigestUtils.shaHex(parentMeetingId + .concat("-").concat(timeStamp.toString()).concat("-") + .concat(params.get("sequence"))); + externalMeetingId = externalHash.concat("-").concat(timeStamp); } // Create the meeting with all passed in parameters. @@ -392,8 +416,14 @@ public class ParamsProcessorUtil { meeting.setModeratorOnlyMessage(moderatorOnlyMessage); } + // Add extra parameters for breakout room + if (isBreakout) { + meeting.setSequence(Integer.parseInt(params.get("sequence"))); + meeting.setParentMeetingId(parentMeetingId); + } + return meeting; - } + } public String getApiVersion() { return apiVersion; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java index 6e2d568fa53c50f2368badb2f947f84db35d6f0f..892d69f33fa9154fad43050868dadef34c494d40 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java @@ -21,11 +21,11 @@ package org.bigbluebutton.api.domain; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; + import org.apache.commons.lang.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,7 +37,9 @@ public class Meeting { private String name; private String extMeetingId; - private String intMeetingId; + private String intMeetingId; + private String parentMeetingId; + private Integer sequence = 0; private Integer duration = 0; private long createdTime = 0; private long startTime = 0; @@ -152,7 +154,15 @@ public class Meeting { public long getCreateTime() { return createdTime; } - + + public Integer setSequence(Integer s) { + return sequence = s; + } + + public Integer getSequence() { + return sequence; + } + public Integer getDuration() { return duration; } @@ -201,6 +211,14 @@ public class Meeting { return intMeetingId; } + public String setParentMeetingId(String p) { + return parentMeetingId = p; + } + + public String getParentMeetingId() { + return parentMeetingId; + } + public String getWebVoice() { return webVoice; } @@ -375,7 +393,7 @@ public class Meeting { public static class Builder { private String name; private String externalId; - private String internalId; + private String internalId; private int maxUsers; private boolean record; private boolean autoStartRecording; @@ -393,18 +411,18 @@ public class Meeting { private String defaultAvatarURL; private long createdTime; private boolean isBreakout; - + public Builder(String externalId, String internalId, long createTime) { this.externalId = externalId; this.internalId = internalId; this.createdTime = createTime; } - + public Builder withName(String name) { this.name = name; return this; } - + public Builder withDuration(int minutes) { duration = minutes; return this; @@ -474,7 +492,7 @@ public class Meeting { isBreakout = b; return this; } - + public Builder withLogoutUrl(String l) { logoutUrl = l; return this; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java index 4c67e5cee9a86d2e224676f4660c018b8b27744b..9bfd168087ef0de3c6559bed909a0e07cc3ed8fc 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java @@ -72,13 +72,14 @@ public class MeetingMessageHandler implements MessageHandler { CreateBreakoutRoomRequest msg = new Gson().fromJson(message, CreateBreakoutRoomRequest.class); for (MessageListener listener : listeners) { listener.handle(new CreateBreakoutRoom( - msg.payload.breakoutId, - msg.payload.parentId, - msg.payload.name, - msg.payload.voiceConfId, - msg.payload.viewerPassword, - msg.payload.moderatorPassword, - msg.payload.durationInMinutes, + msg.payload.breakoutMeetingId, + msg.payload.parentMeetingId, + msg.payload.name, + msg.payload.sequence, + msg.payload.voiceConfId, + msg.payload.viewerPassword, + msg.payload.moderatorPassword, + msg.payload.durationInMinutes, msg.payload.sourcePresentationId, msg.payload.sourcePresentationSlide, msg.payload.record @@ -88,7 +89,7 @@ public class MeetingMessageHandler implements MessageHandler { } else if (EndBreakoutRoomRequest.NAME.equals(messageName)) { EndBreakoutRoomRequest msg = new Gson().fromJson(message, EndBreakoutRoomRequest.class); - log.info("Received an end breakout room request message for breakout meeting id=[{}]", msg.payload.meetingId); + log.info("Received end breakout room request message for breakout meeting id=[{}]", msg.payload.meetingId); for (MessageListener listener : listeners) { listener.handle(new EndBreakoutRoom(msg.payload.meetingId)); } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java index 9be59010d49be788273742f53a77c1eed5221ab7..5b62209ea9b085cfe6241cd09870a64775c8b039 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java @@ -7,7 +7,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.exceptions.JedisConnectionException; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java index 19f8ef24dbf348ac69c40bccf4adfc6bf719f9eb..b665c9b3984642d28a63d84d3070d5ea48e99779 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java @@ -27,12 +27,14 @@ import java.util.Map; import java.util.Set; public interface MessagingService { - void recordMeetingInfo(String meetingId, Map<String, String> info); + void recordMeetingInfo(String meetingId, Map<String, String> info, Map<String, String> breakoutInfo); void destroyMeeting(String meetingID); - void createMeeting(String meetingID, String externalMeetingID, String meetingName, Boolean recorded, - String voiceBridge, Integer duration, Boolean autoStartRecording, - Boolean allowStartStopRecording, String moderatorPass, String viewerPass, - Long createTime, String createDate, Boolean isBreakout); + void createMeeting(String meetingID, String externalMeetingID, + String parentMeetingID, String meetingName, Boolean recorded, + String voiceBridge, Integer duration, Boolean autoStartRecording, + Boolean allowStartStopRecording, String moderatorPass, + String viewerPass, Long createTime, String createDate, + Boolean isBreakout, Integer sequence); void endMeeting(String meetingId); void send(String channel, String message); void sendPolls(String meetingId, String title, String question, String questionType, List<String> answers); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java index 206df68b158ae6bad11593b913744996ed7b09a7..4d4d4e75d66b700e74c449d5a3382446f2ab1303 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java @@ -19,28 +19,27 @@ package org.bigbluebutton.api.messaging; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import javax.imageio.ImageIO; +import java.util.Set; + import org.bigbluebutton.api.messaging.converters.messages.DestroyMeetingMessage; import org.bigbluebutton.api.messaging.converters.messages.EndMeetingMessage; import org.bigbluebutton.api.messaging.converters.messages.RegisterUserMessage; import org.bigbluebutton.common.converters.ToJsonEncoder; +import org.bigbluebutton.common.messages.Constants; import org.bigbluebutton.common.messages.MessagingConstants; +import org.bigbluebutton.common.messages.SendStunTurnInfoReplyMessage; import org.bigbluebutton.messages.CreateMeetingRequest; import org.bigbluebutton.messages.CreateMeetingRequest.CreateMeetingRequestPayload; -import org.bigbluebutton.common.messages.Constants; -import org.bigbluebutton.common.messages.PubSubPingMessage; -import org.bigbluebutton.common.messages.payload.PubSubPingMessagePayload; -import org.bigbluebutton.common.messages.SendStunTurnInfoReplyMessage; import org.bigbluebutton.web.services.turn.StunServer; import org.bigbluebutton.web.services.turn.TurnEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import com.google.gson.Gson; public class RedisMessagingService implements MessagingService { @@ -50,8 +49,8 @@ public class RedisMessagingService implements MessagingService { private MessageSender sender; private ToJsonEncoder encoder = new ToJsonEncoder(); - public void recordMeetingInfo(String meetingId, Map<String, String> info) { - storeService.recordMeetingInfo(meetingId, info); + public void recordMeetingInfo(String meetingId, Map<String, String> info, Map<String, String> breakoutInfo) { + storeService.recordMeetingInfo(meetingId, info, breakoutInfo); } public void destroyMeeting(String meetingID) { @@ -68,22 +67,24 @@ public class RedisMessagingService implements MessagingService { sender.send(MessagingConstants.TO_MEETING_CHANNEL, json); } - public void createMeeting(String meetingID, String externalMeetingID, String meetingName, Boolean recorded, - String voiceBridge, Integer duration, - Boolean autoStartRecording, Boolean allowStartStopRecording, - String moderatorPass, String viewerPass, Long createTime, - String createDate, Boolean isBreakout) { - CreateMeetingRequestPayload payload = new CreateMeetingRequestPayload(meetingID, externalMeetingID, meetingName, - recorded, voiceBridge, duration, - autoStartRecording, allowStartStopRecording, - moderatorPass, viewerPass, createTime, createDate, isBreakout); - CreateMeetingRequest msg = new CreateMeetingRequest(payload); - - Gson gson = new Gson(); - String json = gson.toJson(msg); - log.info("Sending create meeting message to bbb-apps:[{}]", json); - sender.send(MessagingConstants.TO_MEETING_CHANNEL, json); - } + public void createMeeting(String meetingID, String externalMeetingID, + String parentMeetingID, String meetingName, Boolean recorded, + String voiceBridge, Integer duration, Boolean autoStartRecording, + Boolean allowStartStopRecording, String moderatorPass, + String viewerPass, Long createTime, String createDate, + Boolean isBreakout, Integer sequence) { + CreateMeetingRequestPayload payload = new CreateMeetingRequestPayload( + meetingID, externalMeetingID, parentMeetingID, meetingName, + recorded, voiceBridge, duration, autoStartRecording, + allowStartStopRecording, moderatorPass, viewerPass, createTime, + createDate, isBreakout, sequence); + CreateMeetingRequest msg = new CreateMeetingRequest(payload); + + Gson gson = new Gson(); + String json = gson.toJson(msg); + log.info("Sending create meeting message to bbb-apps:[{}]", json); + sender.send(MessagingConstants.TO_MEETING_CHANNEL, json); + } public void endMeeting(String meetingId) { EndMeetingMessage msg = new EndMeetingMessage(meetingId); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java index 1797a97ff2cb8cca950915e8d0c10b3b954deb3f..f27a34d9b928dfee9f95f22d1e0935f1de55b33d 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java @@ -28,24 +28,32 @@ public class RedisStorageService { // CLIENT LIST on redis-cli redisPool = new JedisPool(new GenericObjectPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE, "BbbRed5AppsPub"); - } - public void recordMeetingInfo(String meetingId, Map<String, String> info) { - Jedis jedis = redisPool.getResource(); - try { - for (String key: info.keySet()) { - log.debug("Storing metadata {} = {}", key, info.get(key)); - } + public void recordMeetingInfo(String meetingId, Map<String, String> info, + Map<String, String> breakoutInfo) { + Jedis jedis = redisPool.getResource(); + try { + for (String key : info.keySet()) { + log.debug("Storing metadata {} = {}", key, info.get(key)); + } - log.debug("Saving metadata in {}", meetingId); - jedis.hmset("meeting:info:" + meetingId, info); - } catch (Exception e){ - log.warn("Cannot record the info meeting:"+meetingId,e); - } finally { - jedis.close(); - } - } + log.debug("Saving metadata in {}", meetingId); + jedis.hmset("meeting:info:" + meetingId, info); + + for (String breakoutKey : breakoutInfo.keySet()) { + log.debug("Storing breakout metadata {} = {}", breakoutKey, + breakoutInfo.get(breakoutKey)); + } + + log.debug("Saving breakout metadata in {}", meetingId); + jedis.hmset("meeting:breakout:" + meetingId, breakoutInfo); + } catch (Exception e) { + log.warn("Cannot record the info meeting:" + meetingId, e); + } finally { + jedis.close(); + } + } public void removeMeeting(String meetingId){ Jedis jedis = redisPool.getResource(); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java index 1ef60574382534f96b596635a2458610d82773f7..79aedee164ff5c5b7b117f8a738c731396028d3e 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java @@ -2,9 +2,10 @@ package org.bigbluebutton.api.messaging.messages; public class CreateBreakoutRoom implements IMessage { - public final String breakoutId; - public final String parentId; // The main meeting internal id + public final String meetingId; + public final String parentMeetingId; // The main meeting internal id public final String name; // The name of the breakout room + public final Integer sequence; // The sequence number of the breakout room public final String voiceConfId; // The voice conference id public final String viewerPassword; public final String moderatorPassword; @@ -13,14 +14,15 @@ public class CreateBreakoutRoom implements IMessage { public final Integer sourcePresentationSlide; public final Boolean record; - public CreateBreakoutRoom(String breakoutId, String parentId, String name, - String voiceConfId, String viewerPassword, - String moderatorPassword, Integer duration, + public CreateBreakoutRoom(String meetingId, String parentMeetingId, + String name, Integer sequence, String voiceConfId, + String viewerPassword, String moderatorPassword, Integer duration, String sourcePresentationId, Integer sourcePresentationSlide, Boolean record) { - this.breakoutId = breakoutId; - this.parentId = parentId; + this.meetingId = meetingId; + this.parentMeetingId = parentMeetingId; this.name = name; + this.sequence = sequence; this.voiceConfId = voiceConfId; this.viewerPassword = viewerPassword; this.moderatorPassword = moderatorPassword; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java index dc5c2b36832fe51f70ff02da06ca4eac054282c2..c996f31de9285a042fdfa537d4179b53892aa5a9 100644 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java @@ -1,9 +1,9 @@ package org.bigbluebutton.api.messaging.messages; public class EndBreakoutRoom implements IMessage { - public final String breakoutId; + public final String breakoutMeetingId; - public EndBreakoutRoom(String breakoutId) { - this.breakoutId = breakoutId; + public EndBreakoutRoom(String breakoutMeetingId) { + this.breakoutMeetingId = breakoutMeetingId; } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java index f28a59b3163175bc49475c84066a28a02eda3906..63836e91e40be3ad1b2b9cee6b33a5dd9361b16f 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java @@ -27,6 +27,7 @@ public class PresentationUrlDownloadService { private DocumentConversionService documentConversionService; private String presentationBaseURL; private String presentationDir; + private String BLANK_PRESENTATION; public void processUploadedPresentation(UploadedPresentation uploadedPres) { documentConversionService.processDocument(uploadedPres); @@ -43,11 +44,12 @@ public class PresentationUrlDownloadService { public void extractPage(String sourceMeetingId, String presentationId, Integer presentationSlide, String destinationMeetingId) { - // Construct the source meeting path + // Build the source meeting path File sourceMeetingPath = new File(presentationDir + File.separator + sourceMeetingId + File.separator + sourceMeetingId + File.separator + presentationId); + // Find the source meeting presentation file final String presentationFilter = presentationId; FilenameFilter pdfFilter = new FilenameFilter() { public boolean accept(File dir, String name) { @@ -57,7 +59,7 @@ public class PresentationUrlDownloadService { }; File[] matches = sourceMeetingPath.listFiles(pdfFilter); - if (matches.length != 1) { + if (matches != null && matches.length != 1) { // No PDF presentation was found, we look for an image presentation FilenameFilter imgFlter = new FilenameFilter() { public boolean accept(File dir, String name) { @@ -67,40 +69,44 @@ public class PresentationUrlDownloadService { matches = sourceMeetingPath.listFiles(imgFlter); } - if (matches.length != 1) { - log.info("Not matching PDF file with prefix {} found at {}", + File sourcePresentationFile; + if (matches == null || matches.length != 1) { + log.warn( + "Not matching PDF file with prefix {} found at {}. Using the default blank PDF", sourceMeetingId, sourceMeetingPath); + sourcePresentationFile = new File(BLANK_PRESENTATION); } else { - File sourcePresentationFile = matches[0]; - String filenameExt = FilenameUtils - .getExtension(sourcePresentationFile.getName()); - String presId = generatePresentationId(presentationId); - String newFilename = Util.createNewFilename(presId, filenameExt); - - File uploadDir = createPresentationDirectory(destinationMeetingId, - presentationDir, presId); - String newFilePath = uploadDir.getAbsolutePath() - + File.separatorChar + newFilename; - File newPresentation = new File(newFilePath); - - if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) { - pageExtractor.extractPage(sourcePresentationFile, new File( - newFilePath), presentationSlide); - } else { - try { - FileUtils.copyFile(sourcePresentationFile, newPresentation); - } catch (IOException e) { - log.error("Could not copy presentation {} to {}", - sourcePresentationFile.getAbsolutePath(), - newPresentation.getAbsolutePath()); - e.printStackTrace(); - } + sourcePresentationFile = matches[0]; + } + // Build the target meeting path + String filenameExt = FilenameUtils.getExtension(sourcePresentationFile + .getName()); + String presId = generatePresentationId(presentationId); + String newFilename = Util.createNewFilename(presId, filenameExt); + + File uploadDir = createPresentationDirectory(destinationMeetingId, + presentationDir, presId); + String newFilePath = uploadDir.getAbsolutePath() + File.separatorChar + + newFilename; + File newPresentation = new File(newFilePath); + + if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) { + pageExtractor.extractPage(sourcePresentationFile, new File( + newFilePath), presentationSlide); + } else { + try { + FileUtils.copyFile(sourcePresentationFile, newPresentation); + } catch (IOException e) { + log.error("Could not copy presentation {} to {}", + sourcePresentationFile.getAbsolutePath(), + newPresentation.getAbsolutePath()); + e.printStackTrace(); } - - processUploadedFile(destinationMeetingId, presId, "default-" - + presentationSlide.toString() + "." + filenameExt, - newPresentation); } + + processUploadedFile(destinationMeetingId, presId, "default-" + + presentationSlide.toString() + "." + filenameExt, + newPresentation); } public String generatePresentationId(String name) { @@ -220,4 +226,8 @@ public class PresentationUrlDownloadService { this.documentConversionService = documentConversionService; } + public void setBlankPresentation(String blankPresentation) { + this.BLANK_PRESENTATION = blankPresentation; + } + } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java index 74fc9385c29ea2d4e533e7d1c140b2b299577245..2e917caf12651af408f60dcff4ab6e3fbc734153 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java @@ -21,7 +21,6 @@ package org.bigbluebutton.presentation.imp; import java.io.BufferedReader; import java.io.File; -import java.io.IOException; import java.io.InputStreamReader; import java.util.Timer; import java.util.TimerTask; @@ -29,7 +28,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bigbluebutton.presentation.PageCounter; -import org.bigbluebutton.presentation.imp.ExternalProcessExecutor.InterruptTimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java index a4e3369bd04e2b39a2e6806bb1c912a4cbf9ada2..929dcfb259a9746b1157806d74803f9527257f11 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java @@ -30,7 +30,6 @@ import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java index 3b57b9e295d14d356c8d9dbdb5682c86c5d6fb4f..f9ee00f0ab8c4b205cd52ef5d828e8be6613f1d3 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java @@ -1,13 +1,9 @@ package org.bigbluebutton.presentation.imp; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import org.bigbluebutton.presentation.SvgImageCreator; import org.bigbluebutton.presentation.SupportedFileTypes; +import org.bigbluebutton.presentation.SvgImageCreator; import org.bigbluebutton.presentation.UploadedPresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java index 1fda818a94ec4d7345f36c6d62067d2e8a5467c6..c85d6695660bced734f81bf2ed753ff81d09de43 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java @@ -21,14 +21,13 @@ package org.bigbluebutton.presentation.imp; import java.util.Map; -import org.apache.commons.lang.StringEscapeUtils; import org.bigbluebutton.api.messaging.MessagingConstants; import org.bigbluebutton.api.messaging.MessagingService; import org.bigbluebutton.presentation.ConversionMessageConstants; import org.bigbluebutton.presentation.ConversionUpdateMessage; +import org.bigbluebutton.presentation.ConversionUpdateMessage.MessageBuilder; import org.bigbluebutton.presentation.GeneratedSlidesInfoHelper; import org.bigbluebutton.presentation.UploadedPresentation; -import org.bigbluebutton.presentation.ConversionUpdateMessage.MessageBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java b/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java index d0ee4734b5aeb17e29e394d020add7c2ec058eb4..87a07fffacc516eb1813e005d5535ad9264ac363 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java @@ -19,20 +19,6 @@ package org.bigbluebutton.web.services; -import org.bigbluebutton.api.messaging.MessageListener; -import org.bigbluebutton.api.messaging.MessagingService; -import org.bigbluebutton.api.messaging.MessagingConstants; -import org.bigbluebutton.api.messaging.RedisMessagingService; -import org.bigbluebutton.api.messaging.messages.IMessage; -import org.bigbluebutton.api.messaging.messages.KeepAliveReply; -import org.bigbluebutton.api.messaging.messages.MeetingDestroyed; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Timer; -import java.util.TimerTask; -import java.util.ArrayList; -import java.util.HashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -40,7 +26,12 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import com.google.gson.Gson; +import org.bigbluebutton.api.messaging.MessageListener; +import org.bigbluebutton.api.messaging.MessagingService; +import org.bigbluebutton.api.messaging.messages.IMessage; +import org.bigbluebutton.api.messaging.messages.KeepAliveReply; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class KeepAliveService implements MessageListener { private static Logger log = LoggerFactory.getLogger(KeepAliveService.class);