Merge branch 'master' into refactor-meetings-api
* master: (22 commits) - fix check for breakout room - insert breakout metadata into events.xml - delete breakout metadata from redis Rename meteor method on the client HTML5 - fixed linebreak with lint HTML5 - fixes fullscreen toggle to un-fullscreen Monitoring for number of user is not triggered for breakout rooms. Updated bbb-record rules to detect recording directories. Added new property redirectOnJoin to CreateBreakoutRoomsRequest. Added redirect property to RequestBreakoutJoinURL. Fix handler for 'get_whiteboard_shapes_reply' not calling the cb Store breakout meeting info in redis under "meeting:breakout" key instead of "meeting:info" Using blank-presentation.pdf as default breakout room presentation when no current presentation is set to the parent room. Re-enable recording option for breakout rooms. Store breakout room externalMeetingID to be used for generating join URL. Store breakout room sequence in akka-apps and bbb-web. Revert replacing the deprecated shaHex DigestUtils by sha1Hex and updated API controller. Deleted unused imports from bigbluebutton-apps and bigbluebutton-web Replace the deprecated shaHex DigestUtils by sha1Hex. Send sequence number when creating breakout rooms and correctly generate a unique externalMeetingId for breakout rooms. Breakout rooms are now sorted by their sequence number. Added sequence property breakout room. ...
This commit is contained in:
commit
e1421566be
@ -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",
|
||||
|
@ -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 + "]");
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
|
@ -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 + "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 + "]")
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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", "~")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
createTime, createDate, red5DeskShareIP, red5DeskShareApp,
|
||||
isBreakout, sequence)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.bigbluebutton.red5.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index of a breakout room by its breakoutId
|
||||
*/
|
||||
public function getBreakoutRoomIndex(breakoutId:String):Object {
|
||||
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.breakoutId == breakoutId) {
|
||||
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 internal meeting ID
|
||||
*/
|
||||
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.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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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%"/>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
BIN
bigbluebutton-config/slides/blank-presentation.pdf
Normal file
BIN
bigbluebutton-config/slides/blank-presentation.pdf
Normal file
Binary file not shown.
@ -3,5 +3,4 @@ import sendChat from './methods/sendChat';
|
||||
|
||||
Meteor.methods({
|
||||
sendChat,
|
||||
sendChatMessagetoServer: sendChat, // legacy
|
||||
});
|
||||
|
@ -14,9 +14,9 @@ eventEmitter.on('get_whiteboard_shapes_reply', function (arg) {
|
||||
let whiteboardId = shape.wb_id;
|
||||
addShapeToCollection(meetingId, whiteboardId, shape);
|
||||
}
|
||||
|
||||
return arg.callback();
|
||||
}
|
||||
|
||||
return arg.callback();
|
||||
});
|
||||
|
||||
eventEmitter.on('send_whiteboard_shape_message', function (arg) {
|
||||
|
@ -1,98 +1,96 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import Button from '/imports/ui/components/button/component';
|
||||
import Dropdown from '/imports/ui/components/dropdown/component';
|
||||
import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component';
|
||||
import DropdownContent from '/imports/ui/components/dropdown/content/component';
|
||||
import DropdownList from '/imports/ui/components/dropdown/list/component';
|
||||
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
actionsLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.actionsLabel',
|
||||
|
||||
//defaultMessage: 'Actions',
|
||||
},
|
||||
presentationLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.presentationLabel',
|
||||
defaultMessage: 'Upload a presentation',
|
||||
},
|
||||
initPollLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.initPollLabel',
|
||||
defaultMessage: 'Initiate a poll',
|
||||
},
|
||||
desktopShareLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.desktopShareLabel',
|
||||
defaultMessage: 'Share your screen',
|
||||
},
|
||||
presentationDesc: {
|
||||
id: 'app.actionsBar.actionsDropdown.presentationDesc',
|
||||
defaultMessage: 'Upload your presentation',
|
||||
},
|
||||
initPollDesc: {
|
||||
id: 'app.actionsBar.actionsDropdown.initPollDesc',
|
||||
defaultMessage: 'Initiate a poll',
|
||||
},
|
||||
desktopShareDesc: {
|
||||
id: 'app.actionsBar.actionsDropdown.desktopShareDesc',
|
||||
defaultMessage: 'Share your screen with others',
|
||||
},
|
||||
});
|
||||
|
||||
const presentation = () => {console.log('Should show the uploader component');};
|
||||
|
||||
const polling = () => {console.log('Should initiate a polling');};
|
||||
|
||||
const shareScreen = () => {console.log('Should start screen sharing');};
|
||||
|
||||
class ActionsDropdown extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
return (
|
||||
<Dropdown ref="dropdown">
|
||||
<DropdownTrigger>
|
||||
<Button
|
||||
label={intl.formatMessage(intlMessages.actionsLabel)}
|
||||
icon="circle-add"
|
||||
color="primary"
|
||||
size="lg"
|
||||
circle={true}
|
||||
onClick={() => null}
|
||||
/>
|
||||
</DropdownTrigger>
|
||||
<DropdownContent placement="top left">
|
||||
<DropdownList>
|
||||
<DropdownListItem
|
||||
icon="presentation"
|
||||
label={intl.formatMessage(intlMessages.presentationLabel)}
|
||||
description={intl.formatMessage(intlMessages.presentationDesc)}
|
||||
onClick={presentation.bind(this)}
|
||||
/>
|
||||
|
||||
{/* These icons are unaligned because of the font issue
|
||||
Check it later */}
|
||||
<DropdownListItem
|
||||
icon="polling"
|
||||
label={intl.formatMessage(intlMessages.initPollLabel)}
|
||||
description={intl.formatMessage(intlMessages.initPollDesc)}
|
||||
onClick={polling.bind(this)}
|
||||
/>
|
||||
<DropdownListItem
|
||||
icon="desktop"
|
||||
label={intl.formatMessage(intlMessages.desktopShareLabel)}
|
||||
description={intl.formatMessage(intlMessages.desktopShareDesc)}
|
||||
onClick={shareScreen.bind(this)}
|
||||
/>
|
||||
</DropdownList>
|
||||
</DropdownContent>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(ActionsDropdown);
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import Button from '/imports/ui/components/button/component';
|
||||
import Dropdown from '/imports/ui/components/dropdown/component';
|
||||
import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component';
|
||||
import DropdownContent from '/imports/ui/components/dropdown/content/component';
|
||||
import DropdownList from '/imports/ui/components/dropdown/list/component';
|
||||
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
actionsLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.actionsLabel',
|
||||
},
|
||||
presentationLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.presentationLabel',
|
||||
defaultMessage: 'Upload a presentation',
|
||||
},
|
||||
initPollLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.initPollLabel',
|
||||
defaultMessage: 'Initiate a poll',
|
||||
},
|
||||
desktopShareLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.desktopShareLabel',
|
||||
defaultMessage: 'Share your screen',
|
||||
},
|
||||
presentationDesc: {
|
||||
id: 'app.actionsBar.actionsDropdown.presentationDesc',
|
||||
defaultMessage: 'Upload your presentation',
|
||||
},
|
||||
initPollDesc: {
|
||||
id: 'app.actionsBar.actionsDropdown.initPollDesc',
|
||||
defaultMessage: 'Initiate a poll',
|
||||
},
|
||||
desktopShareDesc: {
|
||||
id: 'app.actionsBar.actionsDropdown.desktopShareDesc',
|
||||
defaultMessage: 'Share your screen with others',
|
||||
},
|
||||
});
|
||||
|
||||
const presentation = () => {console.log('Should show the uploader component');};
|
||||
|
||||
const polling = () => {console.log('Should initiate a polling');};
|
||||
|
||||
const shareScreen = () => {console.log('Should start screen sharing');};
|
||||
|
||||
class ActionsDropdown extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
return (
|
||||
<Dropdown ref="dropdown">
|
||||
<DropdownTrigger>
|
||||
<Button
|
||||
label={intl.formatMessage(intlMessages.actionsLabel)}
|
||||
icon="circle-add"
|
||||
color="primary"
|
||||
size="lg"
|
||||
circle={true}
|
||||
onClick={() => null}
|
||||
/>
|
||||
</DropdownTrigger>
|
||||
<DropdownContent placement="top left">
|
||||
<DropdownList>
|
||||
<DropdownListItem
|
||||
icon="presentation"
|
||||
label={intl.formatMessage(intlMessages.presentationLabel)}
|
||||
description={intl.formatMessage(intlMessages.presentationDesc)}
|
||||
onClick={presentation.bind(this)}
|
||||
/>
|
||||
|
||||
{/* These icons are unaligned because of the font issue
|
||||
Check it later */}
|
||||
<DropdownListItem
|
||||
icon="polling"
|
||||
label={intl.formatMessage(intlMessages.initPollLabel)}
|
||||
description={intl.formatMessage(intlMessages.initPollDesc)}
|
||||
onClick={polling.bind(this)}
|
||||
/>
|
||||
<DropdownListItem
|
||||
icon="desktop"
|
||||
label={intl.formatMessage(intlMessages.desktopShareLabel)}
|
||||
description={intl.formatMessage(intlMessages.desktopShareDesc)}
|
||||
onClick={shareScreen.bind(this)}
|
||||
/>
|
||||
</DropdownList>
|
||||
</DropdownContent>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(ActionsDropdown);
|
||||
|
@ -179,7 +179,7 @@ const sendMessage = (receiverID, message) => {
|
||||
from_color: 0,
|
||||
};
|
||||
|
||||
callServer('sendChatMessagetoServer', messagePayload);
|
||||
callServer('sendChat', messagePayload);
|
||||
|
||||
return messagePayload;
|
||||
};
|
||||
|
@ -51,22 +51,32 @@ const toggleFullScreen = () => {
|
||||
if (document.fullscreenEnabled
|
||||
|| document.mozFullScreenEnabled
|
||||
|| document.webkitFullscreenEnabled) {
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen();
|
||||
} else if (element.msRequestFullscreen) {
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
} else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
|
||||
// If the page is already fullscreen, exit fullscreen
|
||||
if (document.fullscreenElement
|
||||
|| document.webkitFullscreenElement
|
||||
|| document.mozFullScreenElement
|
||||
|| document.msFullscreenElement) {
|
||||
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
|
||||
// If the page is not currently fullscreen, make fullscreen
|
||||
} else {
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen();
|
||||
} else if (element.msRequestFullscreen) {
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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" >
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
|
||||
|
@ -301,15 +301,26 @@ 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()){
|
||||
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()){
|
||||
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 +331,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 +370,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 +520,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 +551,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 +598,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 +611,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());
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
processUploadedFile(destinationMeetingId, presId, "default-"
|
||||
+ presentationSlide.toString() + "." + filenameExt,
|
||||
newPresentation);
|
||||
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);
|
||||
}
|
||||
|
||||
public String generatePresentationId(String name) {
|
||||
@ -220,4 +226,8 @@ public class PresentationUrlDownloadService {
|
||||
this.documentConversionService = documentConversionService;
|
||||
}
|
||||
|
||||
public void setBlankPresentation(String blankPresentation) {
|
||||
this.BLANK_PRESENTATION = blankPresentation;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -50,6 +50,10 @@ module BigBlueButton
|
||||
@redis.hgetall("meeting:info:#{meeting_id}")
|
||||
end
|
||||
|
||||
def breakout_metadata_for(meeting_id)
|
||||
@redis.hgetall("meeting:breakout:#{meeting_id}")
|
||||
end
|
||||
|
||||
def num_events_for(meeting_id)
|
||||
@redis.llen("meeting:#{meeting_id}:recordings")
|
||||
end
|
||||
@ -74,6 +78,10 @@ module BigBlueButton
|
||||
@redis.del("meeting:info:#{meeting_id}")
|
||||
end
|
||||
|
||||
def delete_breakout_metadata_for(meeting_id)
|
||||
@redis.del("meeting:breakout:#{meeting_id}")
|
||||
end
|
||||
|
||||
def build_header(message_type)
|
||||
return {
|
||||
"timestamp" => BigBlueButton.monotonic_clock, #
|
||||
@ -178,11 +186,13 @@ module BigBlueButton
|
||||
result = xml.instruct! :xml, :version => "1.0", :encoding=>"UTF-8"
|
||||
|
||||
meeting_metadata = @redis.metadata_for(meeting_id)
|
||||
breakout_metadata = @redis.breakout_metadata_for(meeting_id)
|
||||
version = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))["bbb_version"]
|
||||
|
||||
if (meeting_metadata != nil)
|
||||
xml.recording(:meeting_id => meeting_id, :bbb_version => version) {
|
||||
xml.metadata(meeting_metadata)
|
||||
xml.breakout(breakout_metadata)
|
||||
msgs = @redis.events_for(meeting_id)
|
||||
msgs.each do |msg|
|
||||
res = @redis.event_info_for(meeting_id, msg)
|
||||
@ -220,6 +230,7 @@ module BigBlueButton
|
||||
@redis.delete_events_for(meeting_id)
|
||||
end
|
||||
@redis.delete_metadata_for(meeting_id)
|
||||
@redis.delete_breakout_metadata_for(meeting_id)
|
||||
end
|
||||
|
||||
def save_events_to_file(directory, result)
|
||||
|
Loading…
Reference in New Issue
Block a user