diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateDefaultPublicGroupChat.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateDefaultPublicGroupChat.scala index 31bac464d2..3d7620fb15 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateDefaultPublicGroupChat.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateDefaultPublicGroupChat.scala @@ -20,7 +20,7 @@ trait CreateDefaultPublicGroupChat { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) val envelope = BbbCoreEnvelope(GroupChatCreatedEvtMsg.NAME, routing) val header = BbbClientMsgHeader(GroupChatCreatedEvtMsg.NAME, meetingId, userId) - val body = GroupChatCreatedEvtMsgBody(correlationId, gc.id, gc.createdBy, gc.name, gc.access, gc.users, msgs) + val body = GroupChatCreatedEvtMsgBody(correlationId, gc.id, gc.createdBy, gc.access, gc.users, msgs) val event = GroupChatCreatedEvtMsg(header, body) BbbCommonEnvCoreMsg(envelope, event) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateGroupChatReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateGroupChatReqMsgHdlr.scala index fddebc0b0b..479f3b7894 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateGroupChatReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/CreateGroupChatReqMsgHdlr.scala @@ -62,7 +62,7 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration { } } - val gc = GroupChatApp.createGroupChat(msg.body.name, msg.body.access, createdBy, users, msgs) + val gc = GroupChatApp.createGroupChat(msg.body.access, createdBy, users, msgs) sendMessages(msg, gc, liveMeeting, bus) val groupChats = state.groupChats.add(gc) @@ -84,12 +84,12 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration { BbbCoreEnvelope(name, routing) } - def makeBody(chatId: String, name: String, + def makeBody(chatId: String, access: String, correlationId: String, createdBy: GroupChatUser, users: Vector[GroupChatUser], msgs: Vector[GroupChatMsgToUser]): GroupChatCreatedEvtMsgBody = { GroupChatCreatedEvtMsgBody(correlationId, chatId, createdBy, - name, access, users, msgs) + access, users, msgs) } val meetingId = liveMeeting.props.meetingProp.intId @@ -102,7 +102,7 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration { val envelope = makeEnvelope(MessageTypes.DIRECT, GroupChatCreatedEvtMsg.NAME, meetingId, userId) val header = makeHeader(GroupChatCreatedEvtMsg.NAME, meetingId, userId) - val body = makeBody(gc.id, gc.name, gc.access, correlationId, gc.createdBy, users, msgs) + val body = makeBody(gc.id, gc.access, correlationId, gc.createdBy, users, msgs) val event = GroupChatCreatedEvtMsg(header, body) val outEvent = BbbCommonEnvCoreMsg(envelope, event) bus.outGW.send(outEvent) @@ -117,7 +117,7 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration { meetingId, userId) val header = makeHeader(GroupChatCreatedEvtMsg.NAME, meetingId, userId) - val body = makeBody(gc.id, gc.name, gc.access, correlationId, gc.createdBy, users, msgs) + val body = makeBody(gc.id, gc.access, correlationId, gc.createdBy, users, msgs) val event = GroupChatCreatedEvtMsg(header, body) val outEvent = BbbCommonEnvCoreMsg(envelope, event) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GetGroupChatsReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GetGroupChatsReqMsgHdlr.scala index 9456e87bea..98ff166a75 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GetGroupChatsReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GetGroupChatsReqMsgHdlr.scala @@ -27,8 +27,8 @@ trait GetGroupChatsReqMsgHdlr { val publicChats = state.groupChats.findAllPublicChats() val privateChats = state.groupChats.findAllPrivateChatsForUser(msg.header.userId) - val pubChats = publicChats map (pc => GroupChatInfo(pc.id, pc.name, pc.access, pc.createdBy, pc.users)) - val privChats = privateChats map (pc => GroupChatInfo(pc.id, pc.name, pc.access, pc.createdBy, pc.users)) + val pubChats = publicChats map (pc => GroupChatInfo(pc.id, pc.access, pc.createdBy, pc.users)) + val privChats = privateChats map (pc => GroupChatInfo(pc.id, pc.access, pc.createdBy, pc.users)) val allChats = pubChats ++ privChats diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GroupChat.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GroupChat.scala index d686f10e7f..d61cc7db67 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GroupChat.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GroupChat.scala @@ -9,10 +9,10 @@ object GroupChatApp { val MAIN_PUBLIC_CHAT = "MAIN-PUBLIC-GROUP-CHAT" - def createGroupChat(chatName: String, access: String, createBy: GroupChatUser, + def createGroupChat(access: String, createBy: GroupChatUser, users: Vector[GroupChatUser], msgs: Vector[GroupChatMessage]): GroupChat = { val gcId = GroupChatFactory.genId() - GroupChatFactory.create(gcId, chatName, access, createBy, users, msgs) + GroupChatFactory.create(gcId, access, createBy, users, msgs) } def toGroupChatMessage(sender: GroupChatUser, msg: GroupChatMsgFromUser): GroupChatMessage = { @@ -46,13 +46,15 @@ object GroupChatApp { def createDefaultPublicGroupChat(): GroupChat = { val createBy = GroupChatUser(SystemUser.ID) - GroupChatFactory.create(MAIN_PUBLIC_CHAT, MAIN_PUBLIC_CHAT, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty) + GroupChatFactory.create(MAIN_PUBLIC_CHAT, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty) } def createTestPublicGroupChat(state: MeetingState2x): MeetingState2x = { val createBy = GroupChatUser(SystemUser.ID) - val defaultPubGroupChat = GroupChatFactory.create("TEST_GROUP_CHAT", "TEST_GROUP_CHAT", - GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty) + val defaultPubGroupChat = GroupChatFactory.create( + "TEST_GROUP_CHAT", + GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty + ) val groupChats = state.groupChats.add(defaultPubGroupChat) state.update(groupChats) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SendGroupChatMessageMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SendGroupChatMessageMsgHdlr.scala index 7f9a103701..aa522c4db6 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SendGroupChatMessageMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SendGroupChatMessageMsgHdlr.scala @@ -1,6 +1,7 @@ package org.bigbluebutton.core.apps.groupchats import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting } @@ -43,17 +44,27 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers { sender <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x) chat <- state.groupChats.find(msg.body.chatId) } yield { - val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg) - val gcs = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm) + val chatIsPrivate = chat.access == GroupChatAccess.PRIVATE; + val userIsAParticipant = chat.users.filter(u => u.id == sender.id).length > 0; - val event = buildGroupChatMessageBroadcastEvtMsg( - liveMeeting.props.meetingProp.intId, - msg.header.userId, msg.body.chatId, gcm - ) + if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) { + val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg) + val gcs = GroupChatApp.addGroupChatMessage(chat, state.groupChats, gcm) - bus.outGW.send(event) + val event = buildGroupChatMessageBroadcastEvtMsg( + liveMeeting.props.meetingProp.intId, + msg.header.userId, msg.body.chatId, gcm + ) + + bus.outGW.send(event) + + state.update(gcs) + } else { + val reason = "User isn't a participant of the chat" + PermissionCheck.ejectUserForFailedPermission(msg.header.meetingId, msg.header.userId, reason, bus.outGW, liveMeeting) + state + } - state.update(gcs) } newState match { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SyncGetGroupChatsInfoMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SyncGetGroupChatsInfoMsgHdlr.scala index f8a63764cc..b7390ede78 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SyncGetGroupChatsInfoMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/SyncGetGroupChatsInfoMsgHdlr.scala @@ -41,7 +41,7 @@ trait SyncGetGroupChatsInfoMsgHdlr { val respMsg = buildSyncGetGroupChatMsgsRespMsg(msgs, pc.id) bus.outGW.send(respMsg) - GroupChatInfo(pc.id, pc.name, pc.access, pc.createdBy, pc.users) + GroupChatInfo(pc.id, pc.access, pc.createdBy, pc.users) }) // publishing a message with the group chat info diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/EjectUserFromMeetingCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/EjectUserFromMeetingCmdMsgHdlr.scala index 72f6ac343f..189f91ecc3 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/EjectUserFromMeetingCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/EjectUserFromMeetingCmdMsgHdlr.scala @@ -51,7 +51,7 @@ trait EjectUserFromMeetingCmdMsgHdlr extends RightsManagementTrait { breakoutModel <- state.breakout } yield { breakoutModel.rooms.values.foreach { room => - room.users.filter(u => u.id == ru.externId + "-" + room.sequence).foreach(user => { + room.users.filter(u => u.id == ru.id + "-" + room.sequence).foreach(user => { eventBus.publish(BigBlueButtonEvent(room.id, EjectUserFromBreakoutInternalMsg(meetingId, room.id, user.id, ejectedBy, reason, EjectReasonCode.EJECT_USER, ban))) }) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GroupChats.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GroupChats.scala index 232b436956..8c5d91b545 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GroupChats.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GroupChats.scala @@ -5,9 +5,9 @@ import org.bigbluebutton.core.util.RandomStringGenerator object GroupChatFactory { def genId(): String = System.currentTimeMillis() + "-" + RandomStringGenerator.randomAlphanumericString(8) - def create(id: String, name: String, access: String, createdBy: GroupChatUser, + def create(id: String, access: String, createdBy: GroupChatUser, users: Vector[GroupChatUser], msgs: Vector[GroupChatMessage]): GroupChat = { - new GroupChat(id, name, access, createdBy, users, msgs) + new GroupChat(id, access, createdBy, users, msgs) } } @@ -23,7 +23,7 @@ case class GroupChats(chats: collection.immutable.Map[String, GroupChat]) { def getAllGroupChatsInMeeting(): Vector[GroupChat] = chats.values.toVector } -case class GroupChat(id: String, name: String, access: String, createdBy: GroupChatUser, +case class GroupChat(id: String, access: String, createdBy: GroupChatUser, users: Vector[GroupChatUser], msgs: Vector[GroupChatMessage]) { def findMsgWithId(id: String): Option[GroupChatMessage] = msgs.find(m => m.id == id) diff --git a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/models/GroupsChatTests.scala b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/models/GroupsChatTests.scala index dd8e75f0bb..5ca56de7ae 100755 --- a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/models/GroupsChatTests.scala +++ b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/models/GroupsChatTests.scala @@ -7,10 +7,9 @@ class GroupsChatTests extends UnitSpec { "A GroupChat" should "be able to add and remove user" in { val gcId = "gc-id" - val chatName = "Public" val userId = "uid-1" val createBy = GroupChatUser("groupId") - val gc = GroupChatFactory.create(gcId, chatName, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty) + val gc = GroupChatFactory.create(gcId, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty) val user = GroupChatUser(userId) val gc2 = gc.add(user) assert(gc2.users.size == 1) @@ -26,8 +25,7 @@ class GroupsChatTests extends UnitSpec { "A GroupChat" should "be able to add, update, and remove msg" in { val createBy = GroupChatUser("groupId") val gcId = "gc-id" - val chatName = "Public" - val gc = GroupChatFactory.create(gcId, chatName, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty) + val gc = GroupChatFactory.create(gcId, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty) val msgId1 = "msgid-1" val ts = System.currentTimeMillis() val hello = "Hello World!" diff --git a/akka-bbb-fsesl/build.sbt b/akka-bbb-fsesl/build.sbt index 0b08fd59ee..5171f7ebef 100755 --- a/akka-bbb-fsesl/build.sbt +++ b/akka-bbb-fsesl/build.sbt @@ -18,7 +18,7 @@ val compileSettings = Seq( "-Xlint", "-Ywarn-dead-code", "-language:_", - "-target:jvm-1.8", + "-target:jvm-1.11", "-encoding", "UTF-8" ), javacOptions ++= List( diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GroupChatMsg.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GroupChatMsg.scala index 502f6cbc97..62f86140c6 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GroupChatMsg.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GroupChatMsg.scala @@ -8,7 +8,7 @@ object GroupChatAccess { case class GroupChatUser(id: String, name: String = "", role: String = "VIEWER") case class GroupChatMsgFromUser(correlationId: String, sender: GroupChatUser, chatEmphasizedText: Boolean = false, message: String) case class GroupChatMsgToUser(id: String, timestamp: Long, correlationId: String, sender: GroupChatUser, chatEmphasizedText: Boolean = false, message: String) -case class GroupChatInfo(id: String, name: String, access: String, createdBy: GroupChatUser, users: Vector[GroupChatUser]) +case class GroupChatInfo(id: String, access: String, createdBy: GroupChatUser, users: Vector[GroupChatUser]) object OpenGroupChatWindowReqMsg { val NAME = "OpenGroupChatWindowReqMsg" } case class OpenGroupChatWindowReqMsg(header: BbbClientMsgHeader, body: OpenGroupChatWindowReqMsgBody) extends StandardMsg @@ -36,14 +36,14 @@ case class GetGroupChatMsgsRespMsgBody(chatId: String, msgs: Vector[GroupChatMsg object CreateGroupChatReqMsg { val NAME = "CreateGroupChatReqMsg" } case class CreateGroupChatReqMsg(header: BbbClientMsgHeader, body: CreateGroupChatReqMsgBody) extends StandardMsg -case class CreateGroupChatReqMsgBody(correlationId: String, name: String, access: String, +case class CreateGroupChatReqMsgBody(correlationId: String, access: String, users: Vector[String], msg: Vector[GroupChatMsgFromUser]) object GroupChatCreatedEvtMsg { val NAME = "GroupChatCreatedEvtMsg" } case class GroupChatCreatedEvtMsg(header: BbbClientMsgHeader, body: GroupChatCreatedEvtMsgBody) extends BbbCoreMsg case class GroupChatCreatedEvtMsgBody(correlationId: String, chatId: String, createdBy: GroupChatUser, - name: String, access: String, - users: Vector[GroupChatUser], msg: Vector[GroupChatMsgToUser]) + access: String, + users: Vector[GroupChatUser], msg: Vector[GroupChatMsgToUser]) object DestroyGroupChatReqMsg { val NAME = "DestroyGroupChatReqMsg" } case class DestroyGroupChatReqMsg(header: BbbClientMsgHeader, body: DestroyGroupChatReqMsgBody) extends StandardMsg diff --git a/bbb-learning-dashboard/package-lock.json b/bbb-learning-dashboard/package-lock.json index 303f534df1..bf813c8f2b 100644 --- a/bbb-learning-dashboard/package-lock.json +++ b/bbb-learning-dashboard/package-lock.json @@ -6251,11 +6251,11 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/ejs": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", - "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", "dependencies": { - "jake": "^10.6.1" + "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" @@ -21236,9 +21236,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "ejs": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.7.tgz", - "integrity": "sha512-BIar7R6abbUxDA3bfXrO4DSgwo8I+fB5/1zgujl3HLLjwd6+9iOnrT+t3grn2qbk9vOgBubXOFwX2m9axoFaGw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", "requires": { "jake": "^10.8.5" } @@ -22007,29 +22007,11 @@ } }, "filelist": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.3.tgz", - "integrity": "sha512-LwjCsruLWQULGYKy7TX0OPtrL9kLpojOFKc5VCTxdFTV7w5zbsgqVKfnkKG7Qgjtq50gKfO56hJv88OfcGb70Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", + "integrity": "sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==", "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "requires": { - "brace-expansion": "^2.0.1" - } - } + "minimatch": "^3.0.4" } }, "filesize": { diff --git a/bbb-pads.placeholder.sh b/bbb-pads.placeholder.sh index 47d3f38b91..7bc05cdf65 100755 --- a/bbb-pads.placeholder.sh +++ b/bbb-pads.placeholder.sh @@ -1 +1 @@ -git clone --branch v1.2.0 --depth 1 https://github.com/bigbluebutton/bbb-pads bbb-pads +git clone --branch v1.2.1 --depth 1 https://github.com/bigbluebutton/bbb-pads bbb-pads diff --git a/bbb-webhooks.placeholder.sh b/bbb-webhooks.placeholder.sh index 57424645b9..5c1d75386a 100755 --- a/bbb-webhooks.placeholder.sh +++ b/bbb-webhooks.placeholder.sh @@ -1 +1 @@ -git clone --branch v2.3.0 --depth 1 https://github.com/bigbluebutton/bbb-webhooks bbb-webhooks +git clone --branch v2.6.0 --depth 1 https://github.com/bigbluebutton/bbb-webhooks bbb-webhooks diff --git a/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js b/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js index 665005e582..58b357f313 100644 --- a/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js +++ b/bigbluebutton-html5/imports/api/group-chat/server/methods/createGroupChat.js @@ -22,7 +22,6 @@ export default function createGroupChat(receiver) { msg: [], users: [receiver.userId], access: CHAT_ACCESS_PRIVATE, - name: receiver.name, }; RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload); diff --git a/bigbluebutton-html5/imports/api/group-chat/server/modifiers/addGroupChat.js b/bigbluebutton-html5/imports/api/group-chat/server/modifiers/addGroupChat.js index 4b7f389d6d..76a064f60b 100644 --- a/bigbluebutton-html5/imports/api/group-chat/server/modifiers/addGroupChat.js +++ b/bigbluebutton-html5/imports/api/group-chat/server/modifiers/addGroupChat.js @@ -9,7 +9,6 @@ export default function addGroupChat(meetingId, chat) { id: Match.Maybe(String), chatId: Match.Maybe(String), correlationId: Match.Maybe(String), - name: String, access: String, createdBy: Object, users: Array, @@ -19,9 +18,8 @@ export default function addGroupChat(meetingId, chat) { const chatDocument = { meetingId, chatId: chat.chatId || chat.id, - name: chat.name, access: chat.access, - users: chat.users.map(u => u.id), + users: chat.users.map((u) => u.id), participants: chat.users, createdBy: chat.createdBy.id, }; @@ -39,9 +37,9 @@ export default function addGroupChat(meetingId, chat) { const { insertedId } = GroupChat.upsert(selector, modifier); if (insertedId) { - Logger.info(`Added group-chat name=${chat.name} meetingId=${meetingId}`); + Logger.info(`Added group-chat chatId=${chatDocument.chatId} meetingId=${meetingId}`); } else { - Logger.info(`Upserted group-chat name=${chat.name} meetingId=${meetingId}`); + Logger.info(`Upserted group-chat chatId=${chatDocument.chatId} meetingId=${meetingId}`); } } catch (err) { Logger.error(`Adding group-chat to collection: ${err}`); diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js index b7d5be1005..f24ec1afeb 100644 --- a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js +++ b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js @@ -4,6 +4,7 @@ import Users from '/imports/api/users'; import userJoin from './userJoin'; import pendingAuthenticationsStore from '../store/pendingAuthentications'; import createDummyUser from '../modifiers/createDummyUser'; +import updateUserConnectionId from '../modifiers/updateUserConnectionId'; import ClientConnections from '/imports/startup/server/ClientConnections'; import upsertValidationState from '/imports/api/auth-token-validation/server/modifiers/upsertValidationState'; @@ -81,6 +82,8 @@ export default function handleValidateAuthToken({ body }, meetingId) { if (!User) { createDummyUser(meetingId, userId, authToken); + }else{ + updateUserConnectionId(meetingId, userId, methodInvocationObject.connection.id); } ClientConnections.add(sessionId, methodInvocationObject.connection); diff --git a/bigbluebutton-html5/imports/api/users/server/modifiers/updateUserConnectionId.js b/bigbluebutton-html5/imports/api/users/server/modifiers/updateUserConnectionId.js new file mode 100644 index 0000000000..2d01e33022 --- /dev/null +++ b/bigbluebutton-html5/imports/api/users/server/modifiers/updateUserConnectionId.js @@ -0,0 +1,32 @@ +import { check } from 'meteor/check'; +import Logger from '/imports/startup/server/logger'; +import Users from '/imports/api/users'; + +export default function updateUserConnectionId(meetingId, userId, connectionId) { + check(meetingId, String); + check(userId, String); + check(connectionId, String); + + const selector = { meetingId, userId }; + + const modifier = { + $set: { + currentConnectionId: connectionId, + connectionIdUpdateTime: new Date().getTime(), + }, + }; + + const User = Users.findOne(selector); + + if (User) { + try { + const updated = Users.update(selector, modifier); + + if (updated) { + Logger.info(`Updated connection user=${userId} connectionid=${connectionId} meeting=${meetingId}`); + } + } catch (err) { + Logger.error(`Updating user connection: ${err}`); + } + } +} diff --git a/bigbluebutton-html5/imports/api/voice-users/server/handlers/leftVoiceUser.js b/bigbluebutton-html5/imports/api/voice-users/server/handlers/leftVoiceUser.js index 8c21da6c52..a36ce99ebd 100755 --- a/bigbluebutton-html5/imports/api/voice-users/server/handlers/leftVoiceUser.js +++ b/bigbluebutton-html5/imports/api/voice-users/server/handlers/leftVoiceUser.js @@ -22,7 +22,7 @@ export default function handleVoiceUpdate({ body }, meetingId) { const isDialInUser = (userId, meetingID) => !!Users.findOne({ meetingId: meetingID, userId, clientType: 'dial-in-user' }); // if the user is dial-in, leaving voice also means leaving userlist - if (isDialInUser(voiceUserId, meetingId)) removeUser(meetingId, intId); + if (isDialInUser(voiceUserId, meetingId)) removeUser(voiceUser, meetingId); return removeVoiceUser(meetingId, voiceUser); } diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index 9f020b7bea..e237c997ac 100755 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -353,6 +353,8 @@ export default withTracker(() => { userId: 1, inactivityCheck: 1, responseDelay: 1, + currentConnectionId: 1, + connectionIdUpdateTime: 1, }; const User = Users.findOne({ intId: credentials.requesterUserId }, { fields }); const meeting = Meetings.findOne({ meetingId }, { @@ -371,6 +373,14 @@ export default withTracker(() => { const ejected = User?.ejected; const ejectedReason = User?.ejectedReason; const meetingEndedReason = meeting?.meetingEndedReason; + const currentConnectionId = User?.currentConnectionId; + const { connectionID, connectionAuthTime } = Auth; + const connectionIdUpdateTime = User?.connectionIdUpdateTime; + + if (currentConnectionId && currentConnectionId !== connectionID && connectionIdUpdateTime > connectionAuthTime) { + Session.set('codeError', 403); + Session.set('errorMessageDescription', 'joined_another_window_reason') + } let userSubscriptionHandler; diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx index 95ead351b3..a016747ba8 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx @@ -284,6 +284,7 @@ class ActionsDropdown extends PureComponent { isMeteorConnected, isDropdownOpen, isMobile, + isRTL, } = this.props; const availableActions = this.getAvailableActions(); @@ -296,7 +297,7 @@ class ActionsDropdown extends PureComponent { || !isMeteorConnected) { return null; } - const customStyles = { top: '-3rem' }; + const customStyles = { top: '-1rem' }; return ( ); diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/container.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/container.jsx index 985c0911f5..c29f13c4c2 100644 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/container.jsx @@ -4,7 +4,7 @@ import Presentations from '/imports/api/presentations'; import PresentationUploaderService from '/imports/ui/components/presentation/presentation-uploader/service'; import PresentationPodService from '/imports/ui/components/presentation-pod/service'; import ActionsDropdown from './component'; -import { layoutSelectInput, layoutDispatch } from '../../layout/context'; +import { layoutSelectInput, layoutDispatch, layoutSelect } from '../../layout/context'; import getFromUserSettings from '/imports/ui/services/users-settings'; import { SMALL_VIEWPORT_BREAKPOINT } from '../../layout/enums'; @@ -14,6 +14,7 @@ const ActionsDropdownContainer = (props) => { const { width: browserWidth } = layoutSelectInput((i) => i.browser); const isMobile = browserWidth <= SMALL_VIEWPORT_BREAKPOINT; const layoutContextDispatch = layoutDispatch(); + const isRTL = layoutSelect((i) => i.isRTL); return ( { sidebarContent, sidebarNavigation, isMobile, + isRTL, ...props, }} /> diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx index e5acc9b835..af0bbe5fa2 100755 --- a/bigbluebutton-html5/imports/ui/components/app/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx @@ -23,6 +23,7 @@ import ManyWebcamsNotifier from '/imports/ui/components/video-provider/many-user import UploaderContainer from '/imports/ui/components/presentation/presentation-uploader/container'; import CaptionsSpeechContainer from '/imports/ui/components/captions/speech/container'; import RandomUserSelectContainer from '/imports/ui/components/common/modal/random-user/container'; +import ScreenReaderAlertContainer from '../screenreader-alert/container'; import NewWebcamContainer from '../webcam/container'; import PresentationAreaContainer from '../presentation/presentation-area/container'; import ScreenshareContainer from '../screenshare/container'; @@ -609,6 +610,7 @@ class App extends Component { > {this.renderActivityCheck()} {this.renderUserInformation()} + diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx index a6bdc71692..d1f56b4805 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx @@ -14,6 +14,7 @@ import { setUserSelectedMicrophone, setUserSelectedListenOnly, } from '../audio-modal/service'; +import { layoutSelect } from '/imports/ui/components/layout/context'; import Service from '../service'; import AppService from '/imports/ui/components/app/service'; @@ -25,7 +26,8 @@ const AudioControlsContainer = (props) => { const { users, lockSettings, userLocks, children, ...newProps } = props; - return ; + const isRTL = layoutSelect((i) => i.isRTL); + return ; }; const handleLeaveAudio = () => { diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx index 90e6d003f7..8ad94b57d6 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx @@ -346,6 +346,7 @@ class InputStreamLiveSelector extends Component { currentInputDeviceId, currentOutputDeviceId, isListenOnly, + isRTL, } = this.props; const inputDeviceList = !isListenOnly @@ -389,10 +390,21 @@ class InputStreamLiveSelector extends Component { label={intl.formatMessage(intlMessages.changeAudioDevice)} hideLabel tabIndex={0} + rotate /> )} actions={dropdownListComplete} + opts={{ + id: 'default-dropdown-menu', + keepMounted: true, + transitionDuration: 0, + elevation: 3, + getContentAnchorEl: null, + fullwidth: 'true', + anchorOrigin: { vertical: 'top', horizontal: isRTL ? 'left' : 'right' }, + transformOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' }, + }} /> ); } diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js index a694fd7d5e..8e01eb414e 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js @@ -8,6 +8,7 @@ import { } from '/imports/ui/stylesheets/styled-components/palette'; import { mdPaddingY, + btnSpacing, } from '/imports/ui/stylesheets/styled-components/general'; import { lineHeightComputed } from '/imports/ui/stylesheets/styled-components/typography'; @@ -48,6 +49,8 @@ const AudioModalButton = styled(Button)` color: black; font-size: 1rem; font-weight: 600; + margin-top: ${btnSpacing}; + line-height: 1.5; } `; @@ -115,6 +118,7 @@ const Title = styled.h2` font-size: 1.3rem; color: ${colorGrayDark}; white-space: normal; + margin: 0; @media ${smallOnly} { font-size: 1rem; diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx index 6c95c85bc2..261e61eb67 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx @@ -2,8 +2,8 @@ import React, { PureComponent } from 'react'; import { defineMessages, injectIntl } from 'react-intl'; import { withModalMounter } from '/imports/ui/components/common/modal/service'; import BBBMenu from "/imports/ui/components/common/menu/component"; -import Button from '/imports/ui/components/common/button/component'; import CreateBreakoutRoomModal from '/imports/ui/components/actions-bar/create-breakout-room/container'; +import Trigger from "/imports/ui/components/common/control-header/right/component"; const intlMessages = defineMessages({ options: { @@ -86,20 +86,16 @@ class BreakoutDropdown extends PureComponent { render() { const { intl, + isRTL, } = this.props; return ( <> null} @@ -112,8 +108,8 @@ class BreakoutDropdown extends PureComponent { elevation: 3, getContentAnchorEl: null, fullwidth: "true", - anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, - transformorigin: { vertical: 'bottom', horizontal: 'left' }, + anchorOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' }, + transformOrigin: { vertical: 'top', horizontal: isRTL ? 'right' : 'left' }, }} actions={this.getAvailableActions()} /> diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx index ee46e81752..8e2bae8667 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx @@ -15,6 +15,7 @@ import Settings from '/imports/ui/services/settings'; import BreakoutDropdown from '/imports/ui/components/breakout-room/breakout-dropdown/component'; import Users from '/imports/api/users'; import Auth from '/imports/ui/services/auth'; +import Header from '/imports/ui/components/common/control-header/component'; const intlMessages = defineMessages({ breakoutTitle: { @@ -549,19 +550,19 @@ class BreakoutRoom extends PureComponent { intl, endAllBreakouts, amIModerator, + isRTL, } = this.props; return ( this.panel = n}> - - { +
{ this.closePanel(); - }} - /> - { amIModerator && ( + }, + }} + customRightButton={amIModerator && ( { @@ -570,9 +571,10 @@ class BreakoutRoom extends PureComponent { }} isMeteorConnected={isMeteorConnected} amIModerator={amIModerator} + isRTL={isRTL} /> - ) } - + )} + /> {this.renderDuration()} {amIModerator ? ( diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx index 55e63ec158..59734a682c 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx @@ -4,7 +4,7 @@ import AudioService from '/imports/ui/components/audio/service'; import AudioManager from '/imports/ui/services/audio-manager'; import BreakoutComponent from './component'; import Service from './service'; -import { layoutDispatch } from '../layout/context'; +import { layoutDispatch, layoutSelect } from '../layout/context'; import Auth from '/imports/ui/services/auth'; import { UsersContext } from '/imports/ui/components/components-data/users-context/context'; import { @@ -18,10 +18,11 @@ const BreakoutContainer = (props) => { const usingUsersContext = useContext(UsersContext); const { users } = usingUsersContext; const amIPresenter = users[Auth.meetingID][Auth.userID].presenter; + const isRTL = layoutSelect((i) => i.isRTL); return ; }; diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/message-form/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/message-form/component.jsx index d18a4fa086..5dcb6997cc 100755 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/message-form/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/message-form/component.jsx @@ -3,6 +3,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import deviceInfo from '/imports/utils/deviceInfo'; import PropTypes from 'prop-types'; import Styled from './styles'; +import { escapeHtml } from '/imports/utils/string-utils'; import { isChatEnabled } from '/imports/ui/services/features'; const propTypes = { @@ -140,7 +141,7 @@ class MessageForm extends PureComponent { handleSendMessage, } = this.props; const { message } = this.state; - let msg = message.trim(); + const msg = message.trim(); if (msg.length < minMessageLength) return; @@ -150,13 +151,7 @@ class MessageForm extends PureComponent { return; } - // Sanitize. See: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript/ - - const div = document.createElement('div'); - div.appendChild(document.createTextNode(msg)); - msg = div.innerHTML; - - handleSendMessage(msg); + handleSendMessage(escapeHtml(msg)); this.setState({ message: '', hasErrors: false }); } diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/styles.js b/bigbluebutton-html5/imports/ui/components/breakout-room/styles.js index 3852e7faa3..ccea3cc3c5 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/styles.js +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/styles.js @@ -10,7 +10,6 @@ import { colorPrimary, colorGray, colorDanger, - colorGrayDark, userListBg, colorWhite, colorGrayLighter, @@ -228,33 +227,6 @@ const Panel = styled(ScrollboxVertical)` height: 100%; `; -const HeaderButton = styled(Button)` - display: flex; - flex-direction: row; - justify-content: space-between; - position: relative; - padding-left: 0; - padding-right: inherit; - background: none !important; - - [dir="rtl"] & { - margin: 0 0 2rem auto; - padding-left: inherit; - padding-right: 0; - } - - & > i { - color: ${colorGrayDark}; - - [dir="rtl"] & { - -webkit-transform: scale(-1, 1); - -moz-transform: scale(-1, 1); - -ms-transform: scale(-1, 1); - -o-transform: scale(-1, 1); - transform: scale(-1, 1); - } - }`; - const Separator = styled.div` position: relative; width: 100%; @@ -264,13 +236,6 @@ const Separator = styled.div` margin: 30px 0px; `; -const Header = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - padding-bottom: ${jumboPaddingY}; -`; - const FlexRow = styled.div` display: flex; flex-wrap: nowrap; @@ -296,8 +261,6 @@ export default { EndButton, Duration, Panel, - HeaderButton, Separator, - Header, FlexRow, }; diff --git a/bigbluebutton-html5/imports/ui/components/captions/component.jsx b/bigbluebutton-html5/imports/ui/components/captions/component.jsx index 7f15414f83..3b5f4a5a82 100644 --- a/bigbluebutton-html5/imports/ui/components/captions/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/captions/component.jsx @@ -8,6 +8,7 @@ import Service from '/imports/ui/components/captions/service'; import Styled from './styles'; import { PANELS, ACTIONS } from '/imports/ui/components/layout/enums'; import browserInfo from '/imports/utils/browserInfo'; +import Header from '/imports/ui/components/common/control-header/component'; const intlMessages = defineMessages({ hide: { @@ -71,54 +72,50 @@ const Captions = ({ return ( - - - { - layoutContextDispatch({ - type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN, - value: false, - }); - layoutContextDispatch({ - type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL, - value: PANELS.NONE, - }); - }} - aria-label={intl.formatMessage(intlMessages.hide)} - label={name} - icon={isRTL ? 'right_arrow' : 'left_arrow'} - /> - - {Service.amICaptionsOwner(ownerId) - ? ( - -