Merge remote-tracking branch 'upstream/v3.0.x-release' into fix-volume-bar-overlap
This commit is contained in:
commit
4c0df76b23
@ -120,13 +120,6 @@ case class CapturePresentationReqInternalMsg(userId: String, parentMeetingId: St
|
||||
*/
|
||||
case class SetPresenterInDefaultPodInternalMsg(presenterId: String) extends InMessage
|
||||
|
||||
/**
|
||||
* Sent by breakout room to parent meeting to obtain padId
|
||||
* @param breakoutId
|
||||
* @param filename
|
||||
*/
|
||||
case class CaptureSharedNotesReqInternalMsg(breakoutId: String, filename: String) extends InMessage
|
||||
|
||||
/**
|
||||
* Sent by GraphqlActionsActor to inform MeetingActor that user disconnected
|
||||
* @param userId
|
||||
|
@ -46,7 +46,7 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
k -> newValue
|
||||
}).toMap
|
||||
|
||||
def addAnnotations(wbId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
||||
def addAnnotations(wbId: String, meetingId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
||||
val wb = getWhiteboard(wbId)
|
||||
|
||||
var annotationsAdded = Array[AnnotationVO]()
|
||||
@ -83,7 +83,7 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
PresAnnotationDAO.insertOrUpdateMap(newAnnotationsMap)
|
||||
PresAnnotationDAO.insertOrUpdateMap(meetingId, annotationsAdded)
|
||||
|
||||
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||
saveWhiteboard(newWb)
|
||||
@ -106,7 +106,7 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
private def cleanEndOrStartProps(props: Map[String, _]): Map[String, _] = {
|
||||
props.get("type") match {
|
||||
case Some("binding") => props - ("x", "y") // Remove 'x' and 'y' for 'binding' type
|
||||
case Some("point") => props - ("boundShapeId", "normalizedAnchor", "isExact") // Remove unwanted properties for 'point' type
|
||||
case Some("point") => props - ("boundShapeId", "normalizedAnchor", "isExact", "isPrecise") // Remove unwanted properties for 'point' type
|
||||
case _ => props
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,7 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
wb.annotationsMap.values.toArray
|
||||
}
|
||||
|
||||
def deleteAnnotations(wbId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
||||
def deleteAnnotations(wbId: String, meetingId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
||||
val wb = getWhiteboard(wbId)
|
||||
|
||||
var annotationsIdsRemoved = Array[String]()
|
||||
@ -143,15 +143,15 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||
saveWhiteboard(updatedWb)
|
||||
|
||||
PresAnnotationDAO.delete(annotationsIdsRemoved)
|
||||
PresAnnotationDAO.delete(meetingId, userId, annotationsIdsRemoved)
|
||||
|
||||
annotationsIdsRemoved
|
||||
}
|
||||
|
||||
def modifyWhiteboardAccess(wbId: String, multiUser: Array[String]) {
|
||||
def modifyWhiteboardAccess(meetingId: String, wbId: String, multiUser: Array[String]) {
|
||||
val wb = getWhiteboard(wbId)
|
||||
val newWb = wb.copy(multiUser = multiUser, oldMultiUser = wb.multiUser, changedModeOn = System.currentTimeMillis())
|
||||
PresPageWritersDAO.updateMultiuser(newWb)
|
||||
PresPageWritersDAO.updateMultiuser(meetingId, newWb)
|
||||
saveWhiteboard(newWb)
|
||||
}
|
||||
|
||||
|
@ -53,8 +53,8 @@ trait BreakoutRoomUsersUpdateMsgHdlr {
|
||||
breakoutRoomUser <- updatedRoom.users
|
||||
u <- RegisteredUsers.findWithBreakoutRoomId(breakoutRoomUser.id, liveMeeting.registeredUsers)
|
||||
} yield u.id
|
||||
UserBreakoutRoomDAO.updateLastBreakoutRoom(usersInRoom, updatedRoom)
|
||||
BreakoutRoomUserDAO.updateUserJoined(usersInRoom, updatedRoom)
|
||||
UserBreakoutRoomDAO.updateLastBreakoutRoom(props.meetingProp.intId, usersInRoom, updatedRoom)
|
||||
BreakoutRoomUserDAO.updateUserJoined(props.meetingProp.intId, usersInRoom, updatedRoom)
|
||||
model.update(updatedRoom)
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
|
||||
)
|
||||
|
||||
//Update database
|
||||
BreakoutRoomUserDAO.updateRoomChanged(msg.body.userId, msg.body.fromBreakoutId, msg.body.toBreakoutId, redirectToHtml5JoinURL)
|
||||
BreakoutRoomUserDAO.updateRoomChanged(meetingId, msg.body.userId, msg.body.fromBreakoutId, msg.body.toBreakoutId, redirectToHtml5JoinURL)
|
||||
|
||||
|
||||
//Send notification to moved User
|
||||
|
@ -30,7 +30,7 @@ trait EjectUserFromBreakoutInternalMsgHdlr {
|
||||
)
|
||||
|
||||
//TODO inform reason
|
||||
UserDAO.softDelete(registeredUser.id)
|
||||
UserDAO.softDelete(registeredUser.meetingId, registeredUser.id)
|
||||
|
||||
// send a system message to force disconnection
|
||||
Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW)
|
||||
|
@ -21,25 +21,19 @@ trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
state
|
||||
} else {
|
||||
for {
|
||||
model <- state.breakout
|
||||
} yield {
|
||||
model.rooms.values.foreach { room =>
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(meetingId, room.id, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)))
|
||||
UserBreakoutRoomDAO.updateLastBreakoutRoom(Vector(), room)
|
||||
}
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)
|
||||
|
||||
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
||||
meetingId,
|
||||
"info",
|
||||
"rooms",
|
||||
"app.toast.breakoutRoomEnded",
|
||||
"Message when the breakout room is ended",
|
||||
Vector()
|
||||
)
|
||||
outGW.send(notifyEvent)
|
||||
NotificationDAO.insert(notifyEvent)
|
||||
|
||||
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
||||
meetingId,
|
||||
"info",
|
||||
"rooms",
|
||||
"app.toast.breakoutRoomEnded",
|
||||
"Message when the breakout room is ended",
|
||||
Vector()
|
||||
)
|
||||
outGW.send(notifyEvent)
|
||||
NotificationDAO.insert(notifyEvent)
|
||||
}
|
||||
BreakoutRoomDAO.updateRoomsEnded(meetingId)
|
||||
state.update(None)
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.core.api.{ CaptureSharedNotesReqInternalMsg, CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
|
||||
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, ExportJob, MessageTypes, PresentationConversionUpdateEvtMsg, PresentationConversionUpdateEvtMsgBody, PresentationConversionUpdateSysPubMsg, PresentationUploadTokenSysPubMsg, PresentationUploadTokenSysPubMsgBody, Routing, StoreExportJobInRedisSysMsg, StoreExportJobInRedisSysMsgBody }
|
||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
|
||||
import org.bigbluebutton.core.apps.presentationpod.PresentationPodsApp
|
||||
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, InternalEventBus }
|
||||
import org.bigbluebutton.core.models.Pads
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||
|
||||
trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
||||
@ -19,12 +22,68 @@ trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
||||
}
|
||||
|
||||
if (liveMeeting.props.breakoutProps.captureNotes) {
|
||||
val filename = liveMeeting.props.breakoutProps.captureNotesFilename
|
||||
val captureNotesEvent = BigBlueButtonEvent(msg.parentId, CaptureSharedNotesReqInternalMsg(msg.breakoutId, filename))
|
||||
eventBus.publish(captureNotesEvent)
|
||||
for {
|
||||
group <- Pads.getGroup(liveMeeting.pads, "notes")
|
||||
} yield {
|
||||
val filename = liveMeeting.props.breakoutProps.captureNotesFilename
|
||||
val userId: String = "system"
|
||||
val jobId: String = s"${msg.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
|
||||
val presentationId = PresentationPodsApp.generatePresentationId(filename)
|
||||
|
||||
if (group.rev > 0) {
|
||||
//Request upload of the sharedNotes of breakoutRoom
|
||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||
outGW.send(buildPresentationUploadTokenSysPubMsg(msg.parentId, userId, presentationUploadToken, filename, presentationId))
|
||||
|
||||
val exportJob = ExportJob(jobId, "PadCaptureJob", filename, filename, group.padId, "", allPages = true, List(), msg.parentId, presentationUploadToken)
|
||||
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
||||
outGW.send(job)
|
||||
} else {
|
||||
// Notify that no content is available
|
||||
val event = buildPresentationConversionUpdateEvtMsg(msg.parentId, presentationId, filename, jobId)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Breakout room {} ended by parent meeting {}.", msg.breakoutId, msg.parentId)
|
||||
sendEndMeetingDueToExpiry(msg.reason, eventBus, outGW, liveMeeting, "system")
|
||||
}
|
||||
|
||||
def buildStoreExportJobInRedisSysMsg(exportJob: ExportJob, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(StoreExportJobInRedisSysMsg.NAME, routing)
|
||||
val body = StoreExportJobInRedisSysMsgBody(exportJob)
|
||||
val header = BbbCoreHeaderWithMeetingId(StoreExportJobInRedisSysMsg.NAME, liveMeeting.props.meetingProp.intId)
|
||||
val event = StoreExportJobInRedisSysMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildPresentationUploadTokenSysPubMsg(parentMeetingId: String, userId: String, presentationUploadToken: String, filename: String, presId: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentMeetingId, userId)
|
||||
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentMeetingId, presId)
|
||||
val event = PresentationUploadTokenSysPubMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildPresentationConversionUpdateEvtMsg(meetingId: String, presentationId: String, presName: String, temporaryPresentationId: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "system")
|
||||
val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PresentationConversionUpdateEvtMsg.NAME, meetingId, "system")
|
||||
|
||||
val body = PresentationConversionUpdateEvtMsgBody(
|
||||
"DEFAULT_PRESENTATION_POD",
|
||||
"204",
|
||||
"not-used",
|
||||
presentationId,
|
||||
presName,
|
||||
temporaryPresentationId
|
||||
)
|
||||
val event = PresentationConversionUpdateEvtMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.UserDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.VoiceUsers
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
@ -17,13 +18,13 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
||||
val reason = "No permission to transfer user to voice breakout."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
} else {
|
||||
processRequest(msg)
|
||||
processTransferUserToMeetingRequest(msg)
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
def processRequest(msg: TransferUserToMeetingRequestMsg) {
|
||||
def processTransferUserToMeetingRequest(msg: TransferUserToMeetingRequestMsg) {
|
||||
if (msg.body.fromMeetingId == liveMeeting.props.meetingProp.intId) {
|
||||
// want to transfer from parent meeting to breakout
|
||||
for {
|
||||
@ -32,6 +33,7 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
||||
from <- getVoiceConf(msg.body.fromMeetingId, model)
|
||||
voiceUser <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
|
||||
} yield {
|
||||
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
|
||||
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
||||
outGW.send(event)
|
||||
}
|
||||
@ -53,6 +55,7 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
|
||||
room <- model.find(msg.body.fromMeetingId)
|
||||
voiceUser <- room.voiceUsers.find(p => p.id == msg.body.userId)
|
||||
} yield {
|
||||
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
|
||||
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
@ -4,8 +4,9 @@ import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.db.{ CaptionLocaleDAO, CaptionTypes }
|
||||
|
||||
class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait {
|
||||
val log = Logging(context.system, getClass)
|
||||
@ -84,6 +85,7 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
|
||||
val event = UpdateCaptionOwnerEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
CaptionLocaleDAO.insertOrUpdateCaptionLocale(liveMeeting.props.meetingProp.intId, locale, CaptionTypes.TYPED, newOwnerId)
|
||||
}
|
||||
|
||||
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
||||
|
@ -10,17 +10,6 @@ trait PadCreateGroupReqMsgHdlr {
|
||||
|
||||
def handle(msg: PadCreateGroupReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
def broadcastEvent(externalId: String, model: String): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadCreateGroupCmdMsg.NAME, routing)
|
||||
val header = BbbCoreHeaderWithMeetingId(PadCreateGroupCmdMsg.NAME, liveMeeting.props.meetingProp.intId)
|
||||
val body = PadCreateGroupCmdMsgBody(externalId, model)
|
||||
val event = PadCreateGroupCmdMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
val padEnabled = msg.body.model match {
|
||||
case "notes" => !liveMeeting.props.meetingProp.disabledFeatures.contains("sharedNotes")
|
||||
case "captions" => !liveMeeting.props.meetingProp.disabledFeatures.contains("captions")
|
||||
@ -29,7 +18,7 @@ trait PadCreateGroupReqMsgHdlr {
|
||||
|
||||
if (padEnabled && !Pads.hasGroup(liveMeeting.pads, msg.body.externalId)) {
|
||||
Pads.addGroup(liveMeeting.pads, msg.body.externalId, msg.body.model, msg.body.name, msg.header.userId)
|
||||
broadcastEvent(msg.body.externalId, msg.body.model)
|
||||
PadslHdlrHelpers.broadcastPadCreateGroupCmdMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.body.externalId, msg.body.model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ trait PadCreatedEvtMsgHdlr {
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
Pads.setPadId(liveMeeting.pads, group.externalId, msg.body.padId)
|
||||
SharedNotesDAO.insert(liveMeeting.props.meetingProp.intId, group, msg.body.padId, msg.body.name)
|
||||
broadcastEvent(group.externalId, group.userId, msg.body.padId, msg.body.name)
|
||||
}
|
||||
|
@ -9,22 +9,12 @@ trait PadGroupCreatedEvtMsgHdlr {
|
||||
this: PadsApp2x =>
|
||||
|
||||
def handle(msg: PadGroupCreatedEvtMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
def broadcastEvent(externalId: String, model: String, name: String, userId: String): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId)
|
||||
val envelope = BbbCoreEnvelope(PadGroupCreatedRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PadGroupCreatedRespMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
|
||||
val body = PadGroupCreatedRespMsgBody(externalId, model, name)
|
||||
val event = PadGroupCreatedRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
||||
case Some(group) => {
|
||||
Pads.setGroupId(liveMeeting.pads, msg.body.externalId, msg.body.groupId)
|
||||
broadcastEvent(msg.body.externalId, group.model, group.name, group.userId)
|
||||
|
||||
//Group was created, now request to create the pad
|
||||
PadslHdlrHelpers.broadcastPadCreateCmdMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.body.groupId, msg.body.externalId)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ trait PadSessionDeletedSysMsgHdlr {
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
SharedNotesSessionDAO.delete(msg.body.userId, msg.body.sessionId)
|
||||
SharedNotesSessionDAO.delete(liveMeeting.props.meetingProp.intId, msg.body.userId, msg.body.sessionId)
|
||||
broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
|
||||
}
|
||||
case _ =>
|
||||
|
@ -24,6 +24,7 @@ trait PadUpdatedSysMsgHdlr {
|
||||
|
||||
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
|
||||
case Some(group) => {
|
||||
Pads.setRev(liveMeeting.pads, group.externalId, msg.body.rev)
|
||||
SharedNotesRevDAO.insert(liveMeeting.props.meetingProp.intId, group.externalId, msg.body.rev, msg.body.userId, msg.body.changeset)
|
||||
broadcastEvent(group.externalId, msg.body.padId, msg.body.userId, msg.body.rev, msg.body.changeset)
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package org.bigbluebutton.core.apps.pads
|
||||
|
||||
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, PadCreateCmdMsg, PadCreateCmdMsgBody, PadCreateGroupCmdMsg, PadCreateGroupCmdMsgBody }
|
||||
import org.bigbluebutton.core.running.OutMsgRouter
|
||||
|
||||
object PadslHdlrHelpers {
|
||||
|
||||
def broadcastPadCreateGroupCmdMsg(outGW: OutMsgRouter, meetingId: String, externalId: String, model: String): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadCreateGroupCmdMsg.NAME, routing)
|
||||
val header = BbbCoreHeaderWithMeetingId(PadCreateGroupCmdMsg.NAME, meetingId)
|
||||
val body = PadCreateGroupCmdMsgBody(externalId, model)
|
||||
val event = PadCreateGroupCmdMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
def broadcastPadCreateCmdMsg(outGW: OutMsgRouter, meetingId: String, groupId: String, name: String): Unit = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(PadCreateCmdMsg.NAME, routing)
|
||||
val header = BbbCoreHeaderWithMeetingId(PadCreateCmdMsg.NAME, meetingId)
|
||||
val body = PadCreateCmdMsgBody(groupId, name)
|
||||
val event = PadCreateCmdMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.ClientSettings
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteMessageMsg
|
||||
import org.bigbluebutton.core.db.PluginDataChannelMessageDAO
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsg
|
||||
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
|
||||
trait PluginDataChannelDeleteMessageMsgHdlr extends HandlerHelpers {
|
||||
trait PluginDataChannelDeleteEntryMsgHdlr extends HandlerHelpers {
|
||||
|
||||
def handle(msg: PluginDataChannelDeleteMessageMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
def handle(msg: PluginDataChannelDeleteEntryMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
|
||||
@ -21,22 +21,23 @@ trait PluginDataChannelDeleteMessageMsgHdlr extends HandlerHelpers {
|
||||
|
||||
if (!pluginsConfig.contains(msg.body.pluginName)) {
|
||||
println(s"Plugin '${msg.body.pluginName}' not found.")
|
||||
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.dataChannel)) {
|
||||
println(s"Data channel '${msg.body.dataChannel}' not found in plugin '${msg.body.pluginName}'.")
|
||||
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.channelName)) {
|
||||
println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.")
|
||||
} else {
|
||||
val hasPermission = for {
|
||||
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).deletePermission
|
||||
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission
|
||||
} yield {
|
||||
deletePermission.toLowerCase match {
|
||||
case "all" => true
|
||||
case "moderator" => user.role == Roles.MODERATOR_ROLE
|
||||
case "presenter" => user.presenter
|
||||
case "sender" => {
|
||||
val senderUserId = PluginDataChannelMessageDAO.getMessageSender(
|
||||
val senderUserId = PluginDataChannelEntryDAO.getMessageSender(
|
||||
meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.dataChannel,
|
||||
msg.body.messageId
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName,
|
||||
msg.body.entryId
|
||||
)
|
||||
senderUserId == msg.header.userId
|
||||
}
|
||||
@ -45,13 +46,14 @@ trait PluginDataChannelDeleteMessageMsgHdlr extends HandlerHelpers {
|
||||
}
|
||||
|
||||
if (!hasPermission.contains(true)) {
|
||||
println(s"No permission to delete in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.")
|
||||
println(s"No permission to delete in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
|
||||
} else {
|
||||
PluginDataChannelMessageDAO.delete(
|
||||
PluginDataChannelEntryDAO.delete(
|
||||
meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.dataChannel,
|
||||
msg.body.messageId
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName,
|
||||
msg.body.entryId
|
||||
)
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelPushEntryMsg
|
||||
import org.bigbluebutton.ClientSettings
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.db.{ PluginDataChannelMessageDAO }
|
||||
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
|
||||
trait PluginDataChannelDispatchMessageMsgHdlr extends HandlerHelpers {
|
||||
trait PluginDataChannelPushEntryMsgHdlr extends HandlerHelpers {
|
||||
|
||||
def handle(msg: PluginDataChannelDispatchMessageMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
def handle(msg: PluginDataChannelPushEntryMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
|
||||
@ -21,11 +21,11 @@ trait PluginDataChannelDispatchMessageMsgHdlr extends HandlerHelpers {
|
||||
|
||||
if (!pluginsConfig.contains(msg.body.pluginName)) {
|
||||
println(s"Plugin '${msg.body.pluginName}' not found.")
|
||||
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.dataChannel)) {
|
||||
println(s"Data channel '${msg.body.dataChannel}' not found in plugin '${msg.body.pluginName}'.")
|
||||
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.channelName)) {
|
||||
println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.")
|
||||
} else {
|
||||
val hasPermission = for {
|
||||
writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).writePermission
|
||||
writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).writePermission
|
||||
} yield {
|
||||
writePermission.toLowerCase match {
|
||||
case "all" => true
|
||||
@ -36,12 +36,13 @@ trait PluginDataChannelDispatchMessageMsgHdlr extends HandlerHelpers {
|
||||
}
|
||||
|
||||
if (!hasPermission.contains(true)) {
|
||||
println(s"No permission to write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.")
|
||||
println(s"No permission to write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
|
||||
} else {
|
||||
PluginDataChannelMessageDAO.insert(
|
||||
PluginDataChannelEntryDAO.insert(
|
||||
meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.dataChannel,
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName,
|
||||
msg.header.userId,
|
||||
msg.body.payloadJson,
|
||||
msg.body.toRoles,
|
@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.bigbluebutton.ClientSettings
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelResetMsg
|
||||
import org.bigbluebutton.core.db.PluginDataChannelMessageDAO
|
||||
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||
@ -21,11 +21,11 @@ trait PluginDataChannelResetMsgHdlr extends HandlerHelpers {
|
||||
|
||||
if (!pluginsConfig.contains(msg.body.pluginName)) {
|
||||
println(s"Plugin '${msg.body.pluginName}' not found.")
|
||||
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.dataChannel)) {
|
||||
println(s"Data channel '${msg.body.dataChannel}' not found in plugin '${msg.body.pluginName}'.")
|
||||
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.channelName)) {
|
||||
println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.")
|
||||
} else {
|
||||
val hasPermission = for {
|
||||
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).deletePermission
|
||||
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission
|
||||
} yield {
|
||||
deletePermission.toLowerCase match {
|
||||
case "all" => true
|
||||
@ -36,12 +36,13 @@ trait PluginDataChannelResetMsgHdlr extends HandlerHelpers {
|
||||
}
|
||||
|
||||
if (!hasPermission.contains(true)) {
|
||||
println(s"No permission to delete (reset) in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.")
|
||||
println(s"No permission to delete (reset) in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
|
||||
} else {
|
||||
PluginDataChannelMessageDAO.reset(
|
||||
PluginDataChannelEntryDAO.reset(
|
||||
meetingId,
|
||||
msg.body.pluginName,
|
||||
msg.body.dataChannel
|
||||
msg.body.channelName,
|
||||
msg.body.subChannelName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,11 @@ package org.bigbluebutton.core.apps.plugin
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.apache.pekko.event.Logging
|
||||
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsgBody
|
||||
|
||||
class PluginHdlrs(implicit val context: ActorContext)
|
||||
extends PluginDataChannelDispatchMessageMsgHdlr
|
||||
with PluginDataChannelDeleteMessageMsgHdlr
|
||||
extends PluginDataChannelPushEntryMsgHdlr
|
||||
with PluginDataChannelDeleteEntryMsgHdlr
|
||||
with PluginDataChannelResetMsgHdlr {
|
||||
|
||||
val log = Logging(context.system, getClass)
|
||||
|
@ -1,10 +1,9 @@
|
||||
package org.bigbluebutton.core.apps.presentationpod
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, CaptureSharedNotesReqInternalMsg }
|
||||
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg }
|
||||
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.apps.presentationpod.PresentationSender
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.db.{ ChatMessageDAO, PresPresentationDAO }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
@ -268,35 +267,8 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
||||
bus.outGW.send(buildBroadcastNewPresFileAvailable(m, liveMeeting))
|
||||
}
|
||||
|
||||
def handle(m: CaptureSharedNotesReqInternalMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
val parentMeetingId = liveMeeting.props.meetingProp.intId
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, parentMeetingId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(PresentationPageConversionStartedEventMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(CaptureSharedNotesReqEvtMsg.NAME, parentMeetingId, "not-used")
|
||||
val body = CaptureSharedNotesReqEvtMsgBody(m.breakoutId, m.filename)
|
||||
val event = CaptureSharedNotesReqEvtMsg(header, body)
|
||||
|
||||
bus.outGW.send(BbbCommonEnvCoreMsg(envelope, event))
|
||||
}
|
||||
|
||||
def handle(m: PresAnnStatusMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
PresPresentationDAO.updateExportToChat(m.body.presId, m.body.status, m.body.pageNumber, m.body.error)
|
||||
bus.outGW.send(buildBroadcastPresAnnStatusMsg(m, liveMeeting))
|
||||
}
|
||||
|
||||
def handle(m: PadCapturePubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
val userId: String = "system"
|
||||
val jobId: String = s"${m.body.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
|
||||
val filename = m.body.filename
|
||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||
val presentationId = PresentationPodsApp.generatePresentationId(m.body.filename)
|
||||
|
||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(m.body.parentMeetingId, userId, presentationUploadToken, filename, presentationId))
|
||||
|
||||
val exportJob = new ExportJob(jobId, JobTypes.CAPTURE_NOTES, filename, filename, m.body.padId, "", true, List(), m.body.parentMeetingId, presentationUploadToken)
|
||||
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
||||
|
||||
bus.outGW.send(job)
|
||||
}
|
||||
}
|
||||
|
@ -13,31 +13,7 @@ trait PresentationConversionUpdatePubMsgHdlr {
|
||||
def handle(msg: PresentationConversionUpdateSysPubMsg, state: MeetingState2x,
|
||||
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||
|
||||
def broadcastEvent(msg: PresentationConversionUpdateSysPubMsg): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(
|
||||
MessageTypes.BROADCAST_TO_MEETING,
|
||||
liveMeeting.props.meetingProp.intId, msg.header.userId
|
||||
)
|
||||
val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(
|
||||
PresentationConversionUpdateEvtMsg.NAME,
|
||||
liveMeeting.props.meetingProp.intId, msg.header.userId
|
||||
)
|
||||
|
||||
val body = PresentationConversionUpdateEvtMsgBody(
|
||||
msg.body.podId,
|
||||
msg.body.messageKey,
|
||||
msg.body.code,
|
||||
msg.body.presentationId,
|
||||
msg.body.presName,
|
||||
msg.body.temporaryPresentationId
|
||||
)
|
||||
val event = PresentationConversionUpdateEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
broadcastEvent(msg)
|
||||
// broadcastEvent(msg)
|
||||
state
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ trait RegisterUserReqMsgHdlr {
|
||||
|
||||
val guestStatus = msg.body.guestStatus
|
||||
|
||||
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
|
||||
val regUser = RegisteredUsers.create(liveMeeting.props.meetingProp.intId, msg.body.intUserId, msg.body.extUserId,
|
||||
msg.body.name, msg.body.role, msg.body.authToken, msg.body.sessionToken,
|
||||
msg.body.avatarURL, ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed,
|
||||
guestStatus, msg.body.excludeFromDashboard, msg.body.enforceLayout, msg.body.customParameters, false)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bigbluebutton.core.apps.users
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.db.UserDAO
|
||||
import org.bigbluebutton.core.models.{ Users2x, VoiceUserState, VoiceUsers }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
|
||||
@ -29,10 +30,10 @@ trait UserConnectedToGlobalAudioMsgHdlr {
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||
} yield {
|
||||
|
||||
val vu = VoiceUserState(
|
||||
intId = user.intId,
|
||||
voiceUserId = user.intId,
|
||||
meetingId = props.meetingProp.intId,
|
||||
callingWith = "flash",
|
||||
callerName = user.name,
|
||||
callerNum = user.name,
|
||||
|
@ -26,7 +26,7 @@ trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait {
|
||||
|
||||
val status = getLevelFromRtt(msg.body.networkRttInMs)
|
||||
|
||||
UserConnectionStatusDAO.updateUserAlive(user.intId, rtt, status)
|
||||
UserConnectionStatusDAO.updateUserAlive(user.meetingId, user.intId, rtt, status)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ trait UserDisconnectedFromGlobalAudioMsgHdlr {
|
||||
for {
|
||||
user <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
|
||||
} yield {
|
||||
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.intId)
|
||||
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId, user.intId)
|
||||
broadcastEvent(user)
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
||||
|
||||
private def handleFailedUserJoin(msg: UserJoinMeetingReqMsg, failReason: String, failReasonCode: String) = {
|
||||
log.info("Ignoring user {} attempt to join in meeting {}. Reason Code: {}, Reason Message: {}", msg.body.userId, msg.header.meetingId, failReasonCode, failReason)
|
||||
UserDAO.updateJoinError(msg.body.userId, failReasonCode, failReason)
|
||||
UserDAO.updateJoinError(msg.header.meetingId, msg.body.userId, failReasonCode, failReason)
|
||||
state
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
||||
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, regUser.id)
|
||||
|
||||
private def clearExpiredUserState(regUser: RegisteredUser) =
|
||||
UserStateDAO.updateExpired(regUser.id, false)
|
||||
UserStateDAO.updateExpired(regUser.meetingId, regUser.id, false)
|
||||
|
||||
private def ForceUserGraphqlReconnection(regUser: RegisteredUser) =
|
||||
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "user_joined", outGW)
|
||||
|
@ -44,7 +44,7 @@ object UsersApp {
|
||||
u <- RegisteredUsers.findWithUserId(guest.guest, liveMeeting.registeredUsers)
|
||||
} yield {
|
||||
RegisteredUsers.setWaitingForApproval(liveMeeting.registeredUsers, u, guest.status)
|
||||
UserStateDAO.updateGuestStatus(guest.guest, guest.status, approvedBy)
|
||||
UserStateDAO.updateGuestStatus(liveMeeting.props.meetingProp.intId, guest.guest, guest.status, approvedBy)
|
||||
// send message to user that he has been approved
|
||||
|
||||
val event = MsgBuilder.buildGuestApprovedEvtMsg(
|
||||
@ -131,7 +131,7 @@ object UsersApp {
|
||||
// println(s"ejectUserFromMeeting will cause a automaticallyAssignPresenter for user=${user}")
|
||||
automaticallyAssignPresenter(outGW, liveMeeting)
|
||||
}
|
||||
UserStateDAO.updateEjected(userId, reason, reasonCode, ejectedBy)
|
||||
UserStateDAO.updateEjected(meetingId, userId, reason, reasonCode, ejectedBy)
|
||||
}
|
||||
|
||||
for {
|
||||
|
@ -75,7 +75,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
||||
}
|
||||
|
||||
private def sendFailedValidateAuthTokenRespMsg(msg: ValidateAuthTokenReqMsg, failReason: String, failReasonCode: String) = {
|
||||
UserDAO.updateJoinError(msg.body.userId, failReasonCode, failReason)
|
||||
UserDAO.updateJoinError(msg.header.meetingId, msg.body.userId, failReasonCode, failReason)
|
||||
|
||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.authToken, false, false, 0,
|
||||
0, failReasonCode, failReason)
|
||||
|
@ -32,7 +32,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
||||
}
|
||||
|
||||
def registerUserInRegisteredUsers() = {
|
||||
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
|
||||
val regUser = RegisteredUsers.create(liveMeeting.props.meetingProp.intId, msg.body.intId, msg.body.voiceUserId,
|
||||
msg.body.callerIdName, Roles.VIEWER_ROLE, msg.body.intId, "", "", userColor,
|
||||
true, true, GuestStatus.WAIT, true, "", Map(), false)
|
||||
RegisteredUsers.add(liveMeeting.registeredUsers, regUser, liveMeeting.props.meetingProp.intId)
|
||||
@ -42,6 +42,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
||||
val newUser = UserState(
|
||||
intId = msg.body.intId,
|
||||
extId = msg.body.voiceUserId,
|
||||
meetingId = liveMeeting.props.meetingProp.intId,
|
||||
name = msg.body.callerIdName,
|
||||
role = Roles.VIEWER_ROLE,
|
||||
guest = true,
|
||||
|
@ -40,14 +40,14 @@ trait UserLeftVoiceConfEvtMsgHdlr {
|
||||
UsersApp.guestWaitingLeft(liveMeeting, user.intId, outGW)
|
||||
}
|
||||
Users2x.remove(liveMeeting.users2x, user.intId)
|
||||
UserDAO.softDelete(user.intId)
|
||||
UserDAO.softDelete(user.meetingId, user.intId)
|
||||
VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId)
|
||||
}
|
||||
|
||||
for {
|
||||
user <- VoiceUsers.findWithVoiceUserId(liveMeeting.voiceUsers, msg.body.voiceUserId)
|
||||
} yield {
|
||||
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.intId)
|
||||
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId, user.intId)
|
||||
broadcastEvent(user)
|
||||
}
|
||||
|
||||
|
@ -238,7 +238,7 @@ object VoiceApp extends SystemConfiguration {
|
||||
): Unit = {
|
||||
for {
|
||||
u <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, userid)
|
||||
oldU <- VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, userid)
|
||||
oldU <- VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, u.meetingId, u.intId)
|
||||
} yield {
|
||||
val event = MsgBuilder.buildEjectUserFromVoiceConfSysMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf, oldU.voiceUserId)
|
||||
outGW.send(event)
|
||||
@ -302,7 +302,6 @@ object VoiceApp extends SystemConfiguration {
|
||||
)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
checkAndEjectOldDuplicateVoiceConfUser(intId, liveMeeting, outGW)
|
||||
|
||||
val isListenOnly = if (callerIdName.startsWith("LISTENONLY")) true else false
|
||||
@ -310,6 +309,7 @@ object VoiceApp extends SystemConfiguration {
|
||||
val voiceUserState = VoiceUserState(
|
||||
intId,
|
||||
voiceUserId,
|
||||
meetingId = liveMeeting.props.meetingProp.intId,
|
||||
callingWith,
|
||||
callerIdName,
|
||||
callerIdNum,
|
||||
@ -393,7 +393,7 @@ object VoiceApp extends SystemConfiguration {
|
||||
for {
|
||||
user <- VoiceUsers.findWithVoiceUserId(liveMeeting.voiceUsers, voiceUserId)
|
||||
} yield {
|
||||
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.intId)
|
||||
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.meetingId, user.intId)
|
||||
broadcastEvent(user)
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ trait VoiceConfCallStateEvtMsgHdlr {
|
||||
outGW.send(msgEvent)
|
||||
|
||||
if (msg.body.userId.nonEmpty) {
|
||||
UserVoiceConfStateDAO.insertOrUpdate(msg.body.userId, msg.body.voiceConf, msg.body.callSession, msg.body.clientSession, msg.body.callState)
|
||||
UserVoiceConfStateDAO.insertOrUpdate(liveMeeting.props.meetingProp.intId, msg.body.userId, msg.body.voiceConf, msg.body.callSession, msg.body.clientSession, msg.body.callState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ trait UserBroadcastCamStartMsgHdlr {
|
||||
val webcam = new WebcamStream(msg.body.stream, msg.header.userId, Set.empty)
|
||||
|
||||
for {
|
||||
_ <- Webcams.addWebcamStream(liveMeeting.webcams, webcam)
|
||||
_ <- Webcams.addWebcamStream(liveMeeting.props.meetingProp.intId, liveMeeting.webcams, webcam)
|
||||
} yield broadcastEvent(meetingId, msg.header.userId, msg.body.stream)
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ trait SendCursorPositionPubMsgHdlr extends RightsManagementTrait {
|
||||
//PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
} else {
|
||||
broadcastEvent(msg)
|
||||
PresPageCursorDAO.insertOrUpdate(msg.body.whiteboardId, msg.header.userId, msg.body.xPercent, msg.body.yPercent)
|
||||
PresPageCursorDAO.insertOrUpdate(msg.body.whiteboardId, liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.xPercent, msg.body.yPercent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class WhiteboardApp2x(implicit val context: ActorContext)
|
||||
isModerator: Boolean
|
||||
): Array[AnnotationVO] = {
|
||||
// println("Received whiteboard annotation. status=[" + status + "], annotationType=[" + annotationType + "]")
|
||||
liveMeeting.wbModel.addAnnotations(whiteboardId, requesterId, annotations, isPresenter, isModerator)
|
||||
liveMeeting.wbModel.addAnnotations(whiteboardId, liveMeeting.props.meetingProp.intId, requesterId, annotations, isPresenter, isModerator)
|
||||
}
|
||||
|
||||
def getWhiteboardAnnotations(whiteboardId: String, liveMeeting: LiveMeeting): Array[AnnotationVO] = {
|
||||
@ -49,7 +49,7 @@ class WhiteboardApp2x(implicit val context: ActorContext)
|
||||
isPresenter: Boolean,
|
||||
isModerator: Boolean
|
||||
): Array[String] = {
|
||||
liveMeeting.wbModel.deleteAnnotations(whiteboardId, requesterId, annotationsIds, isPresenter, isModerator)
|
||||
liveMeeting.wbModel.deleteAnnotations(whiteboardId, liveMeeting.props.meetingProp.intId, requesterId, annotationsIds, isPresenter, isModerator)
|
||||
}
|
||||
|
||||
def getWhiteboardAccess(whiteboardId: String, liveMeeting: LiveMeeting): Array[String] = {
|
||||
@ -57,7 +57,7 @@ class WhiteboardApp2x(implicit val context: ActorContext)
|
||||
}
|
||||
|
||||
def modifyWhiteboardAccess(whiteboardId: String, multiUser: Array[String], liveMeeting: LiveMeeting) {
|
||||
liveMeeting.wbModel.modifyWhiteboardAccess(whiteboardId, multiUser)
|
||||
liveMeeting.wbModel.modifyWhiteboardAccess(liveMeeting.props.meetingProp.intId, whiteboardId, multiUser)
|
||||
}
|
||||
|
||||
def filterWhiteboardMessage(whiteboardId: String, userId: String, liveMeeting: LiveMeeting): Boolean = {
|
||||
|
@ -67,7 +67,7 @@ object BreakoutRoomDAO {
|
||||
userId <- room.assignedUsers
|
||||
(redirectToHtml5JoinURL, redirectJoinURL) <- BreakoutHdlrHelpers.getRedirectUrls(liveMeeting, userId, room.externalId, room.sequence.toString())
|
||||
} yield {
|
||||
BreakoutRoomUserDAO.prepareInsert(room.id, userId, redirectToHtml5JoinURL)
|
||||
BreakoutRoomUserDAO.prepareInsert(room.id, liveMeeting.props.meetingProp.intId, userId, redirectToHtml5JoinURL)
|
||||
}
|
||||
).transactionally)
|
||||
.onComplete {
|
||||
|
@ -11,6 +11,7 @@ import scala.util.{Failure, Success}
|
||||
|
||||
case class BreakoutRoomUserDbModel(
|
||||
breakoutRoomId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
joinURL: String,
|
||||
joinedAt: Option[java.sql.Timestamp],
|
||||
@ -19,19 +20,21 @@ case class BreakoutRoomUserDbModel(
|
||||
|
||||
class BreakoutRoomUserDbTableDef(tag: Tag) extends Table[BreakoutRoomUserDbModel](tag, None, "breakoutRoom_user") {
|
||||
val breakoutRoomId = column[String]("breakoutRoomId", O.PrimaryKey)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val joinURL = column[String]("joinURL")
|
||||
val joinedAt = column[Option[java.sql.Timestamp]]("joinedAt")
|
||||
val assignedAt = column[Option[java.sql.Timestamp]]("assignedAt")
|
||||
override def * = (breakoutRoomId, userId, joinURL, joinedAt, assignedAt) <> (BreakoutRoomUserDbModel.tupled, BreakoutRoomUserDbModel.unapply)
|
||||
override def * = (breakoutRoomId, meetingId, userId, joinURL, joinedAt, assignedAt) <> (BreakoutRoomUserDbModel.tupled, BreakoutRoomUserDbModel.unapply)
|
||||
}
|
||||
|
||||
object BreakoutRoomUserDAO {
|
||||
|
||||
def prepareInsert(breakoutRoomId: String, userId: String, joinURL: String) = {
|
||||
def prepareInsert(breakoutRoomId: String, meetingId: String, userId: String, joinURL: String) = {
|
||||
TableQuery[BreakoutRoomUserDbTableDef].insertOrUpdate(
|
||||
BreakoutRoomUserDbModel(
|
||||
breakoutRoomId = breakoutRoomId,
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
joinURL = joinURL,
|
||||
joinedAt = None,
|
||||
@ -40,8 +43,9 @@ object BreakoutRoomUserDAO {
|
||||
)
|
||||
}
|
||||
|
||||
def prepareDelete(breakoutRoomId: String, userId: String) = {
|
||||
def prepareDelete(breakoutRoomId: String, meetingId: String, userId: String) = {
|
||||
var query = TableQuery[BreakoutRoomUserDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
|
||||
//Sometimes the user is moved before he joined in any room, in this case remove all assignments
|
||||
@ -51,10 +55,10 @@ object BreakoutRoomUserDAO {
|
||||
query.delete
|
||||
}
|
||||
|
||||
def updateRoomChanged(userId: String, fromBreakoutRoomId: String, toBreakoutRoomId: String, joinUrl: String) = {
|
||||
def updateRoomChanged(meetingId: String, userId: String, fromBreakoutRoomId: String, toBreakoutRoomId: String, joinUrl: String) = {
|
||||
DatabaseConnection.db.run(DBIO.seq(
|
||||
BreakoutRoomUserDAO.prepareDelete(fromBreakoutRoomId, userId),
|
||||
BreakoutRoomUserDAO.prepareInsert(toBreakoutRoomId, userId, joinUrl)
|
||||
BreakoutRoomUserDAO.prepareDelete(fromBreakoutRoomId, meetingId, userId),
|
||||
BreakoutRoomUserDAO.prepareInsert(toBreakoutRoomId, meetingId, userId, joinUrl)
|
||||
).transactionally)
|
||||
.onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) changed on breakoutRoom_user table!")
|
||||
@ -62,9 +66,10 @@ object BreakoutRoomUserDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateUserJoined(usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = {
|
||||
def updateUserJoined(meetingId: String, usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[BreakoutRoomUserDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId inSet usersInRoom)
|
||||
.filter(_.breakoutRoomId === breakoutRoom.id)
|
||||
.filter(_.joinedAt.isEmpty)
|
||||
@ -76,9 +81,9 @@ object BreakoutRoomUserDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateUserEjected(userId: String, breakoutRoomId: String) = {
|
||||
def updateUserEjected(meetingId: String, userId: String, breakoutRoomId: String) = {
|
||||
DatabaseConnection.db.run(DBIO.seq(
|
||||
BreakoutRoomUserDAO.prepareDelete(userId, breakoutRoomId),
|
||||
BreakoutRoomUserDAO.prepareDelete(meetingId, userId, breakoutRoomId),
|
||||
).transactionally)
|
||||
.onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) deleted on breakoutRoom_user table!")
|
||||
@ -90,7 +95,7 @@ object BreakoutRoomUserDAO {
|
||||
for {
|
||||
(redirectToHtml5JoinURL, redirectJoinURL) <- BreakoutHdlrHelpers.getRedirectUrls(liveMeeting, userId, room.externalId, room.sequence.toString)
|
||||
} yield {
|
||||
DatabaseConnection.db.run(BreakoutRoomUserDAO.prepareInsert(room.id, userId, redirectToHtml5JoinURL))
|
||||
DatabaseConnection.db.run(BreakoutRoomUserDAO.prepareInsert(room.id, liveMeeting.props.meetingProp.intId, userId, redirectToHtml5JoinURL))
|
||||
.onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on breakoutRoom_user table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting breakoutRoom_user: $e")
|
||||
@ -98,10 +103,11 @@ object BreakoutRoomUserDAO {
|
||||
}
|
||||
}
|
||||
|
||||
// def updateUserJoined(userId: String, breakoutRoomId: String) = {
|
||||
// def updateUserJoined(meetingId: String, userId: String, breakoutRoomId: String) = {
|
||||
// DatabaseConnection.db.run(
|
||||
// TableQuery[BreakoutRoomUserDbTableDef]
|
||||
// .filter(_.breakoutRoomId === breakoutRoomId)
|
||||
// .filter(_.meetingId === meetingId)
|
||||
// .filter(_.userId === userId)
|
||||
// .map(u => u.joinedAt)
|
||||
// .update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
||||
|
@ -9,7 +9,7 @@ case class CaptionDbModel(
|
||||
meetingId: String,
|
||||
captionType: String,
|
||||
userId: String,
|
||||
lang: String,
|
||||
locale: String,
|
||||
captionText: String,
|
||||
createdAt: java.sql.Timestamp
|
||||
)
|
||||
@ -19,10 +19,10 @@ class CaptionTableDef(tag: Tag) extends Table[CaptionDbModel](tag, None, "captio
|
||||
val meetingId = column[String]("meetingId")
|
||||
val captionType = column[String]("captionType")
|
||||
val userId = column[String]("userId")
|
||||
val lang = column[String]("lang")
|
||||
val locale = column[String]("locale")
|
||||
val captionText = column[String]("captionText")
|
||||
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||
def * = (captionId, meetingId, captionType, userId, lang, captionText, createdAt) <> (CaptionDbModel.tupled, CaptionDbModel.unapply)
|
||||
def * = (captionId, meetingId, captionType, userId, locale, captionText, createdAt) <> (CaptionDbModel.tupled, CaptionDbModel.unapply)
|
||||
}
|
||||
|
||||
object CaptionTypes {
|
||||
@ -32,7 +32,7 @@ object CaptionTypes {
|
||||
|
||||
object CaptionDAO {
|
||||
|
||||
def insertOrUpdateAudioCaption(captionId: String, meetingId: String, userId: String, transcript: String, lang: String) = {
|
||||
def insertOrUpdateAudioCaption(captionId: String, meetingId: String, userId: String, transcript: String, locale: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[CaptionTableDef].insertOrUpdate(
|
||||
CaptionDbModel(
|
||||
@ -40,7 +40,7 @@ object CaptionDAO {
|
||||
meetingId = meetingId,
|
||||
captionType = CaptionTypes.AUDIO_TRANSCRIPTION,
|
||||
userId = userId,
|
||||
lang = lang,
|
||||
locale = locale,
|
||||
captionText = transcript,
|
||||
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
|
||||
)
|
||||
@ -68,7 +68,7 @@ object CaptionDAO {
|
||||
SELECT "captionId", "captionText", "createdAt"
|
||||
FROM caption
|
||||
WHERE "meetingId" = ${meetingId}
|
||||
AND lang = ${locale}
|
||||
AND locale = ${locale}
|
||||
AND "captionType" = ${CaptionTypes.TYPED}
|
||||
order by "createdAt" desc
|
||||
limit 2
|
||||
@ -80,13 +80,13 @@ object CaptionDAO {
|
||||
LIMIT 1
|
||||
)
|
||||
RETURNING *)
|
||||
INSERT INTO caption ("captionId", "meetingId", "captionType", "userId", "lang", "captionText", "createdAt")
|
||||
INSERT INTO caption ("captionId", "meetingId", "captionType", "userId", "locale", "captionText", "createdAt")
|
||||
SELECT md5(random()::text || clock_timestamp()::text), ${meetingId}, 'TYPED', ${userId}, ${locale}, ${line}, current_timestamp
|
||||
WHERE NOT EXISTS (SELECT * FROM upsert)
|
||||
AND ${line} NOT IN (SELECT "captionText"
|
||||
FROM caption
|
||||
WHERE "meetingId" = ${meetingId}
|
||||
AND lang = ${locale}
|
||||
AND locale = ${locale}
|
||||
AND "captionType" = ${CaptionTypes.TYPED}
|
||||
order by "createdAt" desc
|
||||
limit 2
|
||||
|
41
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/CaptionLangDAO.scala
Executable file
41
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/CaptionLangDAO.scala
Executable file
@ -0,0 +1,41 @@
|
||||
package org.bigbluebutton.core.db
|
||||
|
||||
import slick.jdbc.PostgresProfile.api._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
case class CaptionLocaleDbModel(
|
||||
meetingId: String,
|
||||
locale: String,
|
||||
captionType: String,
|
||||
ownerUserId: String,
|
||||
updatedAt: java.sql.Timestamp
|
||||
)
|
||||
|
||||
class CaptionLocaleTableDef(tag: Tag) extends Table[CaptionLocaleDbModel](tag, None, "caption_locale") {
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val locale = column[String]("locale", O.PrimaryKey)
|
||||
val captionType = column[String]("captionType", O.PrimaryKey)
|
||||
val ownerUserId = column[String]("ownerUserId")
|
||||
val updatedAt = column[java.sql.Timestamp]("updatedAt")
|
||||
def * = (meetingId, locale, captionType, ownerUserId, updatedAt) <> (CaptionLocaleDbModel.tupled, CaptionLocaleDbModel.unapply)
|
||||
}
|
||||
|
||||
object CaptionLocaleDAO {
|
||||
def insertOrUpdateCaptionLocale(meetingId: String, locale: String, captionType: String, ownerUserId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[CaptionLocaleTableDef].insertOrUpdate(
|
||||
CaptionLocaleDbModel(
|
||||
meetingId = meetingId,
|
||||
locale = locale,
|
||||
captionType = captionType,
|
||||
ownerUserId = ownerUserId,
|
||||
updatedAt = new java.sql.Timestamp(System.currentTimeMillis())
|
||||
)
|
||||
)
|
||||
).onComplete {
|
||||
case Success(_) => DatabaseConnection.logger.debug(s"Upserted caption with ID $meetingId-$locale on CaptionLocale table")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error upserting caption on CaptionLocale: $e")
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,14 @@ import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
case class MeetingSystemColumnsDbModel(
|
||||
loginUrl: Option[String],
|
||||
logoutUrl: Option[String],
|
||||
customLogoUrl: Option[String],
|
||||
bannerText: Option[String],
|
||||
bannerColor: Option[String],
|
||||
)
|
||||
|
||||
case class MeetingDbModel(
|
||||
meetingId: String,
|
||||
extId: String,
|
||||
@ -19,10 +27,7 @@ case class MeetingDbModel(
|
||||
presentationUploadExternalDescription: String,
|
||||
presentationUploadExternalUrl: String,
|
||||
learningDashboardAccessToken: String,
|
||||
logoutUrl: String,
|
||||
customLogoUrl: Option[String],
|
||||
bannerText: Option[String],
|
||||
bannerColor: Option[String],
|
||||
systemColumns: MeetingSystemColumnsDbModel,
|
||||
createdTime: Long,
|
||||
durationInSeconds: Int,
|
||||
endWhenNoModerator: Boolean,
|
||||
@ -45,10 +50,7 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
|
||||
presentationUploadExternalDescription,
|
||||
presentationUploadExternalUrl,
|
||||
learningDashboardAccessToken,
|
||||
logoutUrl,
|
||||
customLogoUrl,
|
||||
bannerText,
|
||||
bannerColor,
|
||||
systemColumns,
|
||||
createdTime,
|
||||
durationInSeconds,
|
||||
endWhenNoModerator,
|
||||
@ -68,10 +70,12 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
|
||||
val presentationUploadExternalDescription = column[String]("presentationUploadExternalDescription")
|
||||
val presentationUploadExternalUrl = column[String]("presentationUploadExternalUrl")
|
||||
val learningDashboardAccessToken = column[String]("learningDashboardAccessToken")
|
||||
val logoutUrl = column[String]("logoutUrl")
|
||||
val loginUrl = column[Option[String]]("loginUrl")
|
||||
val logoutUrl = column[Option[String]]("logoutUrl")
|
||||
val customLogoUrl = column[Option[String]]("customLogoUrl")
|
||||
val bannerText = column[Option[String]]("bannerText")
|
||||
val bannerColor = column[Option[String]]("bannerColor")
|
||||
val systemColumns = (loginUrl, logoutUrl, customLogoUrl, bannerText, bannerColor) <> (MeetingSystemColumnsDbModel.tupled, MeetingSystemColumnsDbModel.unapply)
|
||||
val createdTime = column[Long]("createdTime")
|
||||
val durationInSeconds = column[Int]("durationInSeconds")
|
||||
val endWhenNoModerator = column[Boolean]("endWhenNoModerator")
|
||||
@ -97,19 +101,28 @@ object MeetingDAO {
|
||||
presentationUploadExternalDescription = meetingProps.meetingProp.presentationUploadExternalDescription,
|
||||
presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl,
|
||||
learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken,
|
||||
logoutUrl = meetingProps.systemProps.logoutUrl,
|
||||
customLogoUrl = meetingProps.systemProps.customLogoURL match {
|
||||
case "" => None
|
||||
case logoUrl => Some(logoUrl)
|
||||
},
|
||||
bannerText = meetingProps.systemProps.bannerText match {
|
||||
case "" => None
|
||||
case bannerText => Some(bannerText)
|
||||
},
|
||||
bannerColor = meetingProps.systemProps.bannerColor match {
|
||||
case "" => None
|
||||
case bannerColor => Some(bannerColor)
|
||||
},
|
||||
systemColumns = MeetingSystemColumnsDbModel(
|
||||
loginUrl = meetingProps.systemProps.loginUrl match {
|
||||
case "" => None
|
||||
case loginUrl => Some(loginUrl)
|
||||
},
|
||||
logoutUrl = meetingProps.systemProps.logoutUrl match {
|
||||
case "" => None
|
||||
case logoutUrl => Some(logoutUrl)
|
||||
},
|
||||
customLogoUrl = meetingProps.systemProps.customLogoURL match {
|
||||
case "" => None
|
||||
case logoUrl => Some(logoUrl)
|
||||
},
|
||||
bannerText = meetingProps.systemProps.bannerText match {
|
||||
case "" => None
|
||||
case bannerText => Some(bannerText)
|
||||
},
|
||||
bannerColor = meetingProps.systemProps.bannerColor match {
|
||||
case "" => None
|
||||
case bannerColor => Some(bannerColor)
|
||||
},
|
||||
),
|
||||
createdTime = meetingProps.durationProps.createdTime,
|
||||
durationInSeconds = meetingProps.durationProps.duration * 60,
|
||||
endWhenNoModerator = meetingProps.durationProps.endWhenNoModerator,
|
||||
@ -131,7 +144,7 @@ object MeetingDAO {
|
||||
MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp)
|
||||
MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups)
|
||||
MeetingBreakoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.breakoutProps)
|
||||
TimerDAO.insert(meetingProps.meetingProp.intId)
|
||||
TimerDAO.insert(meetingProps.meetingProp.intId, clientSettings)
|
||||
LayoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp.meetingLayout)
|
||||
MeetingClientSettingsDAO.insert(meetingProps.meetingProp.intId, JsonUtils.mapToJson(clientSettings))
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ case class NotificationDbModel(
|
||||
messageDescription: String,
|
||||
messageValues: JsValue,
|
||||
role: Option[String],
|
||||
userMeetingId: Option[String],
|
||||
userId: Option[String],
|
||||
createdAt: java.sql.Timestamp,
|
||||
)
|
||||
@ -27,9 +28,10 @@ class NotificationDbTableDef(tag: Tag) extends Table[NotificationDbModel](tag, N
|
||||
val messageDescription = column[String]("messageDescription")
|
||||
val messageValues = column[JsValue]("messageValues")
|
||||
val role = column[Option[String]]("role")
|
||||
val userMeetingId = column[Option[String]]("userMeetingId")
|
||||
val userId = column[Option[String]]("userId")
|
||||
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||
override def * = (meetingId, notificationType, icon, messageId, messageDescription, messageValues, role, userId, createdAt) <> (NotificationDbModel.tupled, NotificationDbModel.unapply)
|
||||
override def * = (meetingId, notificationType, icon, messageId, messageDescription, messageValues, role, userMeetingId, userId, createdAt) <> (NotificationDbModel.tupled, NotificationDbModel.unapply)
|
||||
}
|
||||
|
||||
object NotificationDAO {
|
||||
@ -57,6 +59,10 @@ object NotificationDAO {
|
||||
messageDescription,
|
||||
JsonUtils.vectorToJson(messageValues),
|
||||
role,
|
||||
userMeetingId = userId match {
|
||||
case Some(u) => Some(meetingId)
|
||||
case _ => None
|
||||
},
|
||||
userId,
|
||||
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
|
||||
)
|
||||
|
@ -13,11 +13,12 @@ object Permission {
|
||||
val allowedRoles = List("MODERATOR","VIEWER","PRESENTER")
|
||||
}
|
||||
|
||||
case class PluginDataChannelMessageDbModel(
|
||||
case class PluginDataChannelEntryDbModel(
|
||||
meetingId: String,
|
||||
pluginName: String,
|
||||
dataChannel: String,
|
||||
// messageId: Option[String] = None,
|
||||
channelName: String,
|
||||
subChannelName: String,
|
||||
// entryId: Option[String] = None,
|
||||
payloadJson: JsValue,
|
||||
fromUserId: String,
|
||||
toRoles: Option[List[String]],
|
||||
@ -26,28 +27,30 @@ case class PluginDataChannelMessageDbModel(
|
||||
deletedAt: Option[java.sql.Timestamp],
|
||||
)
|
||||
|
||||
class PluginDataChannelMessageDbTableDef(tag: Tag) extends Table[PluginDataChannelMessageDbModel](tag, None, "pluginDataChannelMessage") {
|
||||
class PluginDataChannelEntryDbTableDef(tag: Tag) extends Table[PluginDataChannelEntryDbModel](tag, None, "pluginDataChannelEntry") {
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val pluginName = column[String]("pluginName", O.PrimaryKey)
|
||||
val dataChannel = column[String]("dataChannel", O.PrimaryKey)
|
||||
// val messageId = column[Option[String]]("messageId", O.PrimaryKey) //// The messageId is generated by the database
|
||||
val channelName = column[String]("channelName", O.PrimaryKey)
|
||||
val subChannelName = column[String]("subChannelName")
|
||||
// val entryId = column[Option[String]]("messageId", O.PrimaryKey) //// The messageId is generated by the database
|
||||
val payloadJson = column[JsValue]("payloadJson")
|
||||
val fromUserId = column[String]("fromUserId")
|
||||
val toRoles = column[Option[List[String]]]("toRoles")
|
||||
val toUserIds = column[Option[List[String]]]("toUserIds")
|
||||
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||
val deletedAt = column[Option[java.sql.Timestamp]]("deletedAt")
|
||||
override def * = (meetingId, pluginName, dataChannel, payloadJson, fromUserId, toRoles, toUserIds, createdAt, deletedAt) <> (PluginDataChannelMessageDbModel.tupled, PluginDataChannelMessageDbModel.unapply)
|
||||
override def * = (meetingId, pluginName, channelName, subChannelName, payloadJson, fromUserId, toRoles, toUserIds, createdAt, deletedAt) <> (PluginDataChannelEntryDbModel.tupled, PluginDataChannelEntryDbModel.unapply)
|
||||
}
|
||||
|
||||
object PluginDataChannelMessageDAO {
|
||||
def insert(meetingId: String, pluginName: String, dataChannel: String, senderUserId: String, payloadJson: String, toRoles: List[String], toUserIds: List[String]) = {
|
||||
object PluginDataChannelEntryDAO {
|
||||
def insert(meetingId: String, pluginName: String, channelName: String, subChannelName: String, senderUserId: String, payloadJson: String, toRoles: List[String], toUserIds: List[String]) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PluginDataChannelMessageDbTableDef].forceInsert(
|
||||
PluginDataChannelMessageDbModel(
|
||||
TableQuery[PluginDataChannelEntryDbTableDef].forceInsert(
|
||||
PluginDataChannelEntryDbModel(
|
||||
meetingId = meetingId,
|
||||
pluginName = pluginName,
|
||||
dataChannel = dataChannel,
|
||||
channelName = channelName,
|
||||
subChannelName = subChannelName,
|
||||
payloadJson = JsonUtils.stringToJson(payloadJson),
|
||||
fromUserId = senderUserId,
|
||||
toRoles = toRoles.map(_.toUpperCase).filter(Permission.allowedRoles.contains) match {
|
||||
@ -60,56 +63,62 @@ object PluginDataChannelMessageDAO {
|
||||
)
|
||||
)
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PluginDataChannelMessage table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PluginDataChannelMessage: $e")
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PluginDataChannelEntry table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PluginDataChannelEntry: $e")
|
||||
}
|
||||
}
|
||||
|
||||
def reset(meetingId: String, pluginName: String, dataChannel: String) = {
|
||||
def reset(meetingId: String, pluginName: String,
|
||||
channelName: String, subChannelName: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PluginDataChannelMessageDbTableDef]
|
||||
TableQuery[PluginDataChannelEntryDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.pluginName === pluginName)
|
||||
.filter(_.dataChannel === dataChannel)
|
||||
.filter(_.channelName === channelName)
|
||||
.filter(_.subChannelName === subChannelName)
|
||||
.filter(_.deletedAt.isEmpty)
|
||||
.map(u => (u.deletedAt))
|
||||
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelMessage table!")
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating deleted=now() pluginDataChannelMessage: $e")
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelEntry table!")
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating deleted=now() pluginDataChannelEntry: $e")
|
||||
}
|
||||
}
|
||||
|
||||
def getMessageSender(meetingId: String, pluginName: String, dataChannel: String, messageId: String): String = {
|
||||
def getMessageSender(meetingId: String, pluginName: String, channelName: String,
|
||||
subChannelName: String, entryId: String): String = {
|
||||
val query = sql"""SELECT "fromUserId"
|
||||
FROM "pluginDataChannelMessage"
|
||||
FROM "pluginDataChannelEntry"
|
||||
WHERE "deletedAt" is null
|
||||
AND "meetingId" = ${meetingId}
|
||||
AND "pluginName" = ${pluginName}
|
||||
AND "dataChannel" = ${dataChannel}
|
||||
AND "messageId" = ${messageId}""".as[String].headOption
|
||||
AND "channelName" = ${channelName}
|
||||
AND "subChannelName" = ${subChannelName}
|
||||
AND "entryId" = ${entryId}""".as[String].headOption
|
||||
|
||||
Await.result(DatabaseConnection.db.run(query), Duration.Inf) match {
|
||||
case Some(userId) => userId
|
||||
case None => {
|
||||
logger.debug("Message {} not found in database (maybe it was deleted).", messageId)
|
||||
logger.debug("Message {} not found in database (maybe it was deleted).", entryId)
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def delete(meetingId: String, pluginName: String, dataChannel: String, messageId: String) = {
|
||||
def delete(meetingId: String, pluginName: String,
|
||||
channelName: String, subChannelName: String, entryId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
sqlu"""UPDATE "pluginDataChannelMessage" SET
|
||||
sqlu"""UPDATE "pluginDataChannelEntry" SET
|
||||
"deletedAt" = current_timestamp
|
||||
WHERE "deletedAt" is null
|
||||
AND "meetingId" = ${meetingId}
|
||||
AND "pluginName" = ${pluginName}
|
||||
AND "dataChannel" = ${dataChannel}
|
||||
AND "messageId" = ${messageId}"""
|
||||
AND "channelName" = ${channelName}
|
||||
AND "subChannelName" = ${subChannelName}
|
||||
AND "entryId" = ${entryId}"""
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelMessage table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating deleted=now() pluginDataChannelMessage: $e")
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelEntry table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating deleted=now() pluginDataChannelEntry: $e")
|
||||
}
|
||||
}
|
||||
|
@ -8,25 +8,28 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
case class PollResponseDbModel(
|
||||
pollId: String,
|
||||
optionId: Option[Int],
|
||||
userId: Option[String]
|
||||
pollId: String,
|
||||
optionId: Option[Int],
|
||||
meetingId: Option[String],
|
||||
userId: Option[String]
|
||||
)
|
||||
|
||||
class PollResponseDbTableDef(tag: Tag) extends Table[PollResponseDbModel](tag, None, "poll_response") {
|
||||
val pollId = column[String]("pollId")
|
||||
val optionId = column[Option[Int]]("optionId")
|
||||
val meetingId = column[Option[String]]("meetingId")
|
||||
val userId = column[Option[String]]("userId")
|
||||
val * = (pollId, optionId, userId) <> (PollResponseDbModel.tupled, PollResponseDbModel.unapply)
|
||||
val * = (pollId, optionId, meetingId, userId) <> (PollResponseDbModel.tupled, PollResponseDbModel.unapply)
|
||||
}
|
||||
|
||||
object PollResponseDAO {
|
||||
def insert(poll: Poll, userId: String, seqOptionIds: Seq[Int]) = {
|
||||
def insert(poll: Poll, meetingId: String, userId: String, seqOptionIds: Seq[Int]) = {
|
||||
|
||||
//Clear previous responses of the user and add all
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PollResponseDbTableDef]
|
||||
.filter(_.pollId === poll.id)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.delete
|
||||
).onComplete {
|
||||
@ -39,6 +42,9 @@ object PollResponseDAO {
|
||||
PollResponseDbModel(
|
||||
pollId = poll.id,
|
||||
optionId = Some(optionId),
|
||||
meetingId = {
|
||||
if (poll.isSecret) None else Some(meetingId)
|
||||
},
|
||||
userId = {
|
||||
if (poll.isSecret) None else Some(userId)
|
||||
}
|
||||
@ -57,6 +63,7 @@ object PollResponseDAO {
|
||||
PollResponseDbModel(
|
||||
pollId = poll.id,
|
||||
optionId = None,
|
||||
meetingId = Some(meetingId),
|
||||
userId = Some(userId)
|
||||
)
|
||||
)
|
||||
|
@ -8,6 +8,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
case class PresAnnotationDbModel(
|
||||
annotationId: String,
|
||||
pageId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
annotationInfo: String,
|
||||
lastHistorySequence: Int,
|
||||
@ -17,17 +18,18 @@ case class PresAnnotationDbModel(
|
||||
class PresAnnotationDbTableDef(tag: Tag) extends Table[PresAnnotationDbModel](tag, None, "pres_annotation") {
|
||||
val annotationId = column[String]("annotationId", O.PrimaryKey)
|
||||
val pageId = column[String]("pageId")
|
||||
val meetingId = column[String]("meetingId")
|
||||
val userId = column[String]("userId")
|
||||
val annotationInfo = column[String]("annotationInfo")
|
||||
val lastHistorySequence = column[Int]("lastHistorySequence")
|
||||
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
|
||||
// def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade)
|
||||
def * = (annotationId, pageId, userId, annotationInfo, lastHistorySequence, lastUpdatedAt) <> (PresAnnotationDbModel.tupled, PresAnnotationDbModel.unapply)
|
||||
def * = (annotationId, pageId, meetingId, userId, annotationInfo, lastHistorySequence, lastUpdatedAt) <> (PresAnnotationDbModel.tupled, PresAnnotationDbModel.unapply)
|
||||
}
|
||||
|
||||
object PresAnnotationDAO {
|
||||
def insertOrUpdate(annotation: AnnotationVO, annotationDiff: AnnotationVO) = {
|
||||
PresAnnotationHistoryDAO.insert(annotationDiff).onComplete {
|
||||
def insertOrUpdate(meetingId: String, annotation: AnnotationVO, annotationDiff: AnnotationVO) = {
|
||||
PresAnnotationHistoryDAO.insert(meetingId, annotationDiff).onComplete {
|
||||
case Success(sequence) => {
|
||||
DatabaseConnection.logger.debug(s"Sequence generated to PresAnnotationHistory record: $sequence")
|
||||
DatabaseConnection.db.run(
|
||||
@ -35,6 +37,7 @@ object PresAnnotationDAO {
|
||||
PresAnnotationDbModel(
|
||||
annotationId = annotation.id,
|
||||
pageId = annotation.wbId,
|
||||
meetingId = meetingId,
|
||||
userId = annotation.userId,
|
||||
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
|
||||
lastHistorySequence = sequence.getOrElse(0),
|
||||
@ -51,11 +54,12 @@ object PresAnnotationDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def prepareInsertOrUpdate(annotation: AnnotationVO) = {
|
||||
def prepareInsertOrUpdate(meetingId: String, annotation: AnnotationVO) = {
|
||||
TableQuery[PresAnnotationDbTableDef].insertOrUpdate(
|
||||
PresAnnotationDbModel(
|
||||
annotationId = annotation.id,
|
||||
pageId = annotation.wbId,
|
||||
meetingId = meetingId,
|
||||
userId = annotation.userId,
|
||||
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
|
||||
lastHistorySequence = 0,
|
||||
@ -64,12 +68,12 @@ object PresAnnotationDAO {
|
||||
)
|
||||
}
|
||||
|
||||
def insertOrUpdateMap(annotations: Map[String, AnnotationVO]) = {
|
||||
def insertOrUpdateMap(meetingId: String, annotations: Array[AnnotationVO]) = {
|
||||
DatabaseConnection.db.run(
|
||||
DBIO.sequence(
|
||||
annotations.map { annotation =>
|
||||
prepareInsertOrUpdate(annotation._2)
|
||||
}
|
||||
prepareInsertOrUpdate(meetingId, annotation)
|
||||
}.toVector
|
||||
).transactionally
|
||||
)
|
||||
.onComplete {
|
||||
@ -80,14 +84,14 @@ object PresAnnotationDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def delete(wbId: String, userId: String, annotationId: String) = {
|
||||
PresAnnotationHistoryDAO.delete(wbId, userId, annotationId).onComplete {
|
||||
def delete(wbId: String, meetingId: String, userId: String, annotationId: String) = {
|
||||
PresAnnotationHistoryDAO.delete(wbId, meetingId, userId, annotationId).onComplete {
|
||||
case Success(sequence) => {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PresAnnotationDbTableDef]
|
||||
.filter(_.annotationId === annotationId)
|
||||
.map(a => (a.annotationInfo, a.lastHistorySequence, a.lastUpdatedAt))
|
||||
.update("", sequence.getOrElse(0), new java.sql.Timestamp(System.currentTimeMillis()))
|
||||
.map(a => (a.annotationInfo, a.lastHistorySequence, a.meetingId, a.userId, a.lastUpdatedAt))
|
||||
.update("", sequence.getOrElse(0), meetingId, userId, new java.sql.Timestamp(System.currentTimeMillis()))
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated annotationInfo=null on PresAnnotation table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating annotationInfo=null PresAnnotation: $e")
|
||||
@ -97,12 +101,12 @@ object PresAnnotationDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def delete(annotationIds: Array[String]) = {
|
||||
def delete(meetingId: String, userId: String, annotationIds: Array[String]) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PresAnnotationDbTableDef]
|
||||
.filter(_.annotationId inSet annotationIds)
|
||||
.map(a => (a.annotationInfo, a.lastHistorySequence, a.lastUpdatedAt))
|
||||
.update("", 0, new java.sql.Timestamp(System.currentTimeMillis()))
|
||||
.map(a => (a.annotationInfo, a.lastHistorySequence, a.meetingId, a.userId, a.lastUpdatedAt))
|
||||
.update("", 0, meetingId, userId, new java.sql.Timestamp(System.currentTimeMillis()))
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated annotationInfo=null on PresAnnotation table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating annotationInfo=null PresAnnotation: $e")
|
||||
|
@ -7,6 +7,7 @@ case class PresAnnotationHistoryDbModel(
|
||||
sequence: Option[Int] = None,
|
||||
annotationId: String,
|
||||
pageId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
annotationInfo: String
|
||||
// lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
|
||||
@ -16,16 +17,17 @@ class PresAnnotationHistoryDbTableDef(tag: Tag) extends Table[PresAnnotationHist
|
||||
val sequence = column[Option[Int]]("sequence", O.PrimaryKey, O.AutoInc)
|
||||
val annotationId = column[String]("annotationId")
|
||||
val pageId = column[String]("pageId")
|
||||
val meetingId = column[String]("meetingId")
|
||||
val userId = column[String]("userId")
|
||||
val annotationInfo = column[String]("annotationInfo")
|
||||
// val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
|
||||
// def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade)
|
||||
def * = (sequence, annotationId, pageId, userId, annotationInfo) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply)
|
||||
def * = (sequence, annotationId, pageId, meetingId, userId, annotationInfo) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply)
|
||||
}
|
||||
|
||||
object PresAnnotationHistoryDAO {
|
||||
|
||||
def insert(annotationDiff: AnnotationVO) = {
|
||||
def insert(meetingId: String, annotationDiff: AnnotationVO) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PresAnnotationHistoryDbTableDef].returning(
|
||||
TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence)
|
||||
@ -33,13 +35,14 @@ object PresAnnotationHistoryDAO {
|
||||
None,
|
||||
annotationId = annotationDiff.id,
|
||||
pageId = annotationDiff.wbId,
|
||||
meetingId = meetingId,
|
||||
userId = annotationDiff.userId,
|
||||
annotationInfo = JsonUtils.mapToJson(annotationDiff.annotationInfo).compactPrint
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def delete(wbId: String, userId: String, annotationId: String) = {
|
||||
def delete(wbId: String, meetingId: String, userId: String, annotationId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PresAnnotationHistoryDbTableDef].returning(
|
||||
TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence)
|
||||
@ -47,6 +50,7 @@ object PresAnnotationHistoryDAO {
|
||||
None,
|
||||
annotationId = annotationId,
|
||||
pageId = wbId,
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
annotationInfo = ""
|
||||
)
|
||||
|
@ -8,6 +8,7 @@ import scala.util.{ Failure, Success }
|
||||
|
||||
case class PresPageCursorDbModel(
|
||||
pageId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
xPercent: Double,
|
||||
yPercent: Double,
|
||||
@ -16,11 +17,11 @@ case class PresPageCursorDbModel(
|
||||
|
||||
class PresPageCursorDbTableDef(tag: Tag) extends Table[PresPageCursorDbModel](tag, None, "pres_page_cursor") {
|
||||
override def * = (
|
||||
pageId, userId, xPercent, yPercent, lastUpdatedAt
|
||||
pageId, meetingId, userId, xPercent, yPercent, lastUpdatedAt
|
||||
) <> (PresPageCursorDbModel.tupled, PresPageCursorDbModel.unapply)
|
||||
def pk = primaryKey("pres_page_cursor_pkey", (pageId, userId))
|
||||
val pageId = column[String]("pageId")
|
||||
val userId = column[String]("userId")
|
||||
val pageId = column[String]("pageId", O.PrimaryKey)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val xPercent = column[Double]("xPercent")
|
||||
val yPercent = column[Double]("yPercent")
|
||||
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
|
||||
@ -28,11 +29,12 @@ class PresPageCursorDbTableDef(tag: Tag) extends Table[PresPageCursorDbModel](ta
|
||||
|
||||
object PresPageCursorDAO {
|
||||
|
||||
def insertOrUpdate(pageId: String, userId: String, xPercent: Double, yPercent: Double) = {
|
||||
def insertOrUpdate(pageId: String, meetingId: String, userId: String, xPercent: Double, yPercent: Double) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[PresPageCursorDbTableDef].insertOrUpdate(
|
||||
PresPageCursorDbModel(
|
||||
pageId = pageId,
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
xPercent = xPercent,
|
||||
yPercent = yPercent,
|
||||
|
@ -7,34 +7,36 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class PresPageWritersDbModel(
|
||||
pageId: String,
|
||||
userId: String,
|
||||
changedModeOn: Long,
|
||||
pageId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
changedModeOn: Long,
|
||||
)
|
||||
|
||||
class PresPageWritersDbTableDef(tag: Tag) extends Table[PresPageWritersDbModel](tag, None, "pres_page_writers") {
|
||||
override def * = (
|
||||
pageId, userId, changedModeOn) <> (PresPageWritersDbModel.tupled, PresPageWritersDbModel.unapply)
|
||||
def pk = primaryKey("pres_page_writers_pkey", (pageId, userId))
|
||||
val pageId = column[String]("pageId")
|
||||
val userId = column[String]("userId")
|
||||
pageId, meetingId, userId, changedModeOn) <> (PresPageWritersDbModel.tupled, PresPageWritersDbModel.unapply)
|
||||
val pageId = column[String]("pageId", O.PrimaryKey)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val changedModeOn = column[Long]("changedModeOn")
|
||||
}
|
||||
|
||||
object PresPageWritersDAO {
|
||||
|
||||
def updateMultiuser(whiteboard: Whiteboard) = {
|
||||
def updateMultiuser(meetingId: String, whiteboard: Whiteboard) = {
|
||||
|
||||
val deleteQuery = TableQuery[PresPageWritersDbTableDef]
|
||||
.filter(_.pageId === whiteboard.id)
|
||||
|
||||
if(whiteboard.multiUser.length > 0) {
|
||||
deleteQuery.filter(_.meetingId === meetingId)
|
||||
deleteQuery.filterNot(_.userId inSet whiteboard.multiUser)
|
||||
}
|
||||
|
||||
DatabaseConnection.db.run(deleteQuery.delete).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Users deleted from Whiteboard ${whiteboard.id}")
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting users from whiteboard: $e")
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Users deleted from pres_page_writers ${whiteboard.id}")
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting users from pres_page_writers: $e")
|
||||
}
|
||||
|
||||
for {
|
||||
@ -44,6 +46,7 @@ object PresPageWritersDAO {
|
||||
TableQuery[PresPageWritersDbTableDef].insertOrUpdate(
|
||||
PresPageWritersDbModel(
|
||||
pageId = whiteboard.id,
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
changedModeOn = whiteboard.changedModeOn
|
||||
)
|
||||
|
@ -35,10 +35,11 @@ object SharedNotesSessionDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def delete(intId: String, sessionId: String) = {
|
||||
def delete(meetingId: String, userId: String, sessionId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[SharedNotesSessionDbTableDef]
|
||||
.filter(_.userId === intId)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.filter(_.sessionId === sessionId)
|
||||
.delete
|
||||
).onComplete {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.bigbluebutton.core.db
|
||||
|
||||
import org.bigbluebutton.ClientSettings.{getConfigPropertyValueByPathAsBooleanOrElse, getConfigPropertyValueByPathAsIntOrElse}
|
||||
import org.bigbluebutton.core.apps.TimerModel
|
||||
import org.bigbluebutton.core.apps.TimerModel.{getAccumulated, getEndedAt, getIsActive, getRunning, getStartedAt, getStopwatch, getTime, getTrack}
|
||||
import slick.jdbc.PostgresProfile.api._
|
||||
@ -33,25 +34,31 @@ class TimerDbTableDef(tag: Tag) extends Table[TimerDbModel](tag, None, "timer")
|
||||
}
|
||||
|
||||
object TimerDAO {
|
||||
def insert(meetingId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[TimerDbTableDef].insertOrUpdate(
|
||||
TimerDbModel(
|
||||
meetingId = meetingId,
|
||||
stopwatch = true,
|
||||
running = false,
|
||||
active = false,
|
||||
time = 300000,
|
||||
accumulated = 0,
|
||||
startedOn = 0,
|
||||
endedOn = 0,
|
||||
songTrack = "noTrack",
|
||||
def insert(meetingId: String, clientSettings: Map[String, Object]) = {
|
||||
val timerEnabled = getConfigPropertyValueByPathAsBooleanOrElse(clientSettings, "public.timer.enabled", alternativeValue = true)
|
||||
if(timerEnabled) {
|
||||
val timerDefaultTimeInMinutes = getConfigPropertyValueByPathAsIntOrElse(clientSettings, "public.timer.time", 5)
|
||||
val timerDefaultTimeInMilli = timerDefaultTimeInMinutes * 60000
|
||||
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[TimerDbTableDef].insertOrUpdate(
|
||||
TimerDbModel(
|
||||
meetingId = meetingId,
|
||||
stopwatch = true,
|
||||
running = false,
|
||||
active = false,
|
||||
time = timerDefaultTimeInMilli,
|
||||
accumulated = 0,
|
||||
startedOn = 0,
|
||||
endedOn = 0,
|
||||
songTrack = "noTrack",
|
||||
)
|
||||
)
|
||||
)
|
||||
).onComplete {
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on Timer table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting Timer: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def update(meetingId: String, timerModel: TimerModel) = {
|
||||
|
@ -7,17 +7,19 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success }
|
||||
|
||||
case class UserBreakoutRoomDbModel(
|
||||
breakoutRoomId: String,
|
||||
userId: String,
|
||||
isDefaultName: Boolean,
|
||||
sequence: Int,
|
||||
shortName: String,
|
||||
currentlyInRoom: Boolean,
|
||||
breakoutRoomId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
isDefaultName: Boolean,
|
||||
sequence: Int,
|
||||
shortName: String,
|
||||
currentlyInRoom: Boolean,
|
||||
)
|
||||
|
||||
class UserBreakoutRoomDbTableDef(tag: Tag) extends Table[UserBreakoutRoomDbModel](tag, None, "user_breakoutRoom") {
|
||||
override def * = (
|
||||
breakoutRoomId, userId, isDefaultName, sequence, shortName, currentlyInRoom) <> (UserBreakoutRoomDbModel.tupled, UserBreakoutRoomDbModel.unapply)
|
||||
breakoutRoomId, meetingId, userId, isDefaultName, sequence, shortName, currentlyInRoom) <> (UserBreakoutRoomDbModel.tupled, UserBreakoutRoomDbModel.unapply)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val breakoutRoomId = column[String]("breakoutRoomId")
|
||||
val isDefaultName = column[Boolean]("isDefaultName")
|
||||
@ -28,11 +30,11 @@ class UserBreakoutRoomDbTableDef(tag: Tag) extends Table[UserBreakoutRoomDbModel
|
||||
|
||||
object UserBreakoutRoomDAO {
|
||||
|
||||
def updateLastBreakoutRoom(userId: String, breakoutRoom: BreakoutRoom2x) = {
|
||||
|
||||
def updateLastBreakoutRoom(meetingId: String, userId: String, breakoutRoom: BreakoutRoom2x) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserBreakoutRoomDbTableDef].insertOrUpdate(
|
||||
UserBreakoutRoomDbModel(
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
breakoutRoomId = breakoutRoom.id,
|
||||
isDefaultName = breakoutRoom.isDefaultName,
|
||||
@ -49,10 +51,11 @@ object UserBreakoutRoomDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateLastBreakoutRoom(usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = {
|
||||
def updateLastBreakoutRoom(meetingId:String, usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = {
|
||||
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserBreakoutRoomDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filterNot(_.userId inSet usersInRoom)
|
||||
.filter(_.breakoutRoomId === breakoutRoom.id)
|
||||
.map(u_bk => u_bk.currentlyInRoom)
|
||||
@ -68,6 +71,7 @@ object UserBreakoutRoomDAO {
|
||||
} yield {
|
||||
TableQuery[UserBreakoutRoomDbTableDef].insertOrUpdate(
|
||||
UserBreakoutRoomDbModel(
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
breakoutRoomId = breakoutRoom.id,
|
||||
isDefaultName = breakoutRoom.isDefaultName,
|
||||
|
@ -7,25 +7,28 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class UserCameraDbModel(
|
||||
streamId: String,
|
||||
streamId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
)
|
||||
|
||||
class UserCameraDbTableDef(tag: Tag) extends Table[UserCameraDbModel](tag, None, "user_camera") {
|
||||
override def * = (
|
||||
streamId, userId) <> (UserCameraDbModel.tupled, UserCameraDbModel.unapply)
|
||||
streamId, meetingId, userId) <> (UserCameraDbModel.tupled, UserCameraDbModel.unapply)
|
||||
val streamId = column[String]("streamId", O.PrimaryKey)
|
||||
val meetingId = column[String]("meetingId")
|
||||
val userId = column[String]("userId")
|
||||
}
|
||||
|
||||
object UserCameraDAO {
|
||||
|
||||
def insert(webcam: WebcamStream) = {
|
||||
def insert(meetingId: String, webcam: WebcamStream) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserCameraDbTableDef].forceInsert(
|
||||
UserCameraDbModel(
|
||||
streamId = webcam.streamId,
|
||||
userId = webcam.userId
|
||||
meetingId = meetingId,
|
||||
userId = webcam.userId,
|
||||
)
|
||||
)
|
||||
).onComplete {
|
||||
|
@ -14,7 +14,7 @@ case class UserClientSettingsDbModel(
|
||||
|
||||
class UserClientSettingsDbTableDef(tag: Tag) extends Table[UserClientSettingsDbModel](tag, "user_clientSettings") {
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val meetingId = column[String]("meetingId")
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userClientSettingsJson = column[JsValue]("userClientSettingsJson")
|
||||
|
||||
override def * : ProvenShape[UserClientSettingsDbModel] = (userId, meetingId, userClientSettingsJson) <> (UserClientSettingsDbModel.tupled, UserClientSettingsDbModel.unapply)
|
||||
|
@ -18,7 +18,7 @@ class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatu
|
||||
userId, meetingId, connectionAliveAt, networkRttInMs, status, statusUpdatedAt
|
||||
) <> (UserConnectionStatusDbModel.tupled, UserConnectionStatusDbModel.unapply)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val meetingId = column[String]("meetingId")
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
|
||||
val networkRttInMs = column[Option[Double]]("networkRttInMs")
|
||||
val status = column[String]("status")
|
||||
@ -45,9 +45,10 @@ object UserConnectionStatusDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateUserAlive(userId: String, rtt: Option[Double], status: String) = {
|
||||
def updateUserAlive(meetingId: String, userId: String, rtt: Option[Double], status: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserConnectionStatusDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(t => (t.connectionAliveAt, t.networkRttInMs, t.status, t.statusUpdatedAt))
|
||||
.update(
|
||||
|
@ -7,27 +7,30 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
case class UserCustomParameterDbModel(
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
parameter: String,
|
||||
value: String
|
||||
)
|
||||
|
||||
class UserCustomParameterDbTableDef(tag: Tag) extends Table[UserCustomParameterDbModel](tag, "user_customParameter") {
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val parameter = column[String]("parameter", O.PrimaryKey)
|
||||
val value = column[String]("value")
|
||||
|
||||
override def * : ProvenShape[UserCustomParameterDbModel] = (userId, parameter, value) <> (UserCustomParameterDbModel.tupled, UserCustomParameterDbModel.unapply)
|
||||
override def * : ProvenShape[UserCustomParameterDbModel] = (meetingId, userId, parameter, value) <> (UserCustomParameterDbModel.tupled, UserCustomParameterDbModel.unapply)
|
||||
}
|
||||
|
||||
object UserCustomParameterDAO {
|
||||
def insert(userId: String, customParameters: Map[String, String]) = {
|
||||
def insert(meetingId: String, userId: String, customParameters: Map[String, String]) = {
|
||||
DatabaseConnection.db.run(DBIO.sequence(
|
||||
for {
|
||||
parameter <- customParameters
|
||||
} yield {
|
||||
TableQuery[UserCustomParameterDbTableDef].insertOrUpdate(
|
||||
UserCustomParameterDbModel(
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
parameter = parameter._1,
|
||||
value = parameter._2
|
||||
|
@ -6,9 +6,9 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class UserDbModel(
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
extId: String,
|
||||
meetingId: String,
|
||||
name: String,
|
||||
role: String,
|
||||
avatar: String = "",
|
||||
@ -32,10 +32,11 @@ case class UserDbModel(
|
||||
|
||||
class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
||||
override def * = (
|
||||
userId,extId,meetingId,name,role,avatar,color, sessionToken, authToken, authed,joined,joinErrorCode, joinErrorMessage, banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard, enforceLayout) <> (UserDbModel.tupled, UserDbModel.unapply)
|
||||
meetingId,userId,extId,name,role,avatar,color, sessionToken, authToken, authed,joined,joinErrorCode,
|
||||
joinErrorMessage, banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard, enforceLayout) <> (UserDbModel.tupled, UserDbModel.unapply)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val extId = column[String]("extId")
|
||||
val meetingId = column[String]("meetingId")
|
||||
val name = column[String]("name")
|
||||
val role = column[String]("role")
|
||||
val avatar = column[String]("avatar")
|
||||
@ -89,7 +90,7 @@ object UserDAO {
|
||||
case Success(rowsAffected) => {
|
||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!")
|
||||
UserConnectionStatusDAO.insert(meetingId, regUser.id)
|
||||
UserCustomParameterDAO.insert(regUser.id, regUser.customParameters)
|
||||
UserCustomParameterDAO.insert(meetingId, regUser.id, regUser.customParameters)
|
||||
UserClientSettingsDAO.insert(regUser.id, meetingId)
|
||||
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
|
||||
}
|
||||
@ -100,6 +101,7 @@ object UserDAO {
|
||||
def update(regUser: RegisteredUser) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserDbTableDef]
|
||||
.filter(_.meetingId === regUser.meetingId)
|
||||
.filter(_.userId === regUser.id)
|
||||
.map(u => (u.guest, u.guestStatus, u.role, u.authed, u.joined, u.banned, u.loggedOut))
|
||||
.update((regUser.guest, regUser.guestStatus, regUser.role, regUser.authed, regUser.joined, regUser.banned, regUser.loggedOut))
|
||||
@ -113,6 +115,7 @@ object UserDAO {
|
||||
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserDbTableDef]
|
||||
.filter(_.meetingId === voiceUserState.meetingId)
|
||||
.filter(_.userId === voiceUserState.intId)
|
||||
.map(u => (u.guest, u.guestStatus, u.authed, u.joined))
|
||||
.update((false, "ALLOW", true, true))
|
||||
@ -122,9 +125,10 @@ object UserDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateJoinError(userId: String, joinErrorCode: String, joinErrorMessage: String) = {
|
||||
def updateJoinError(meetingId: String, userId: String, joinErrorCode: String, joinErrorMessage: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.joined, u.joinErrorCode, u.joinErrorMessage))
|
||||
.update((false, Some(joinErrorCode), Some(joinErrorMessage)))
|
||||
@ -134,11 +138,11 @@ object UserDAO {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def softDelete(intId: String) = {
|
||||
def softDelete(meetingId: String, userId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserDbTableDef]
|
||||
.filter(_.userId === intId)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.loggedOut))
|
||||
.update((true))
|
||||
).onComplete {
|
||||
@ -159,6 +163,62 @@ object UserDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def transferUserToBreakoutRoomAsAudioOnly(userId: String, meetingIdFrom: String, meetingIdTo: String) = {
|
||||
|
||||
//Create a copy of the user using the same userId, but with the meetingId of the breakoutRoom
|
||||
//The user will be flagged by `transferredFromParentMeeting=true`
|
||||
DatabaseConnection.db.run(
|
||||
sqlu"""
|
||||
WITH upsert AS (
|
||||
UPDATE "user"
|
||||
SET "loggedOut"=false
|
||||
where "userId" = ${userId}
|
||||
and "meetingId" = ${meetingIdTo}
|
||||
RETURNING *)
|
||||
insert into "user"("meetingId","userId","extId","name","role","guest","authed","guestStatus","locked",
|
||||
"color","loggedOut","expired","ejected","joined","registeredOn","transferredFromParentMeeting","clientType")
|
||||
select
|
||||
${meetingIdTo} as "meetingId",
|
||||
"userId",
|
||||
"extId",
|
||||
"name",
|
||||
"role",
|
||||
true as "guest",
|
||||
true as "authed",
|
||||
'ALLOW' as "guestStatus",
|
||||
false as "locked",
|
||||
"color",
|
||||
"loggedOut",
|
||||
"expired",
|
||||
"ejected",
|
||||
"joined",
|
||||
"registeredOn",
|
||||
true as "transferredFromParentMeeting",
|
||||
'dial-in-user' as "clientType"
|
||||
from "user"
|
||||
where "userId" = ${userId}
|
||||
and "meetingId" = ${meetingIdFrom}
|
||||
and NOT EXISTS (SELECT * FROM upsert)
|
||||
"""
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in user (transferredFromParentMeeting) table")
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting user (transferredFromParentMeeting): $e")
|
||||
}
|
||||
|
||||
//Set user as loggedOut in the old meeting (if it is from transferred origin)
|
||||
DatabaseConnection.db.run(
|
||||
sqlu"""update "user"
|
||||
set "loggedOut" = true
|
||||
where "userId" = ${userId}
|
||||
and "meetingId" = ${meetingIdFrom}
|
||||
and "transferredFromParentMeeting" is true
|
||||
"""
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated in user (transferredFromParentMeeting) table")
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating user (transferredFromParentMeeting): $e")
|
||||
}
|
||||
}
|
||||
|
||||
def permanentlyDeleteAllFromMeeting(meetingId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserDbTableDef]
|
||||
|
@ -7,6 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
case class UserReactionDbModel(
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
reactionEmoji: String,
|
||||
durationInSeconds: Int,
|
||||
@ -14,19 +15,21 @@ case class UserReactionDbModel(
|
||||
)
|
||||
|
||||
class UserReactionDbTableDef(tag: Tag) extends Table[UserReactionDbModel](tag, "user_reaction") {
|
||||
val meetingId = column[String]("meetingId")
|
||||
val userId = column[String]("userId")
|
||||
val reactionEmoji = column[String]("reactionEmoji")
|
||||
val durationInSeconds = column[Int]("durationInSeconds")
|
||||
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||
|
||||
override def * : ProvenShape[UserReactionDbModel] = (userId, reactionEmoji, durationInSeconds, createdAt) <> (UserReactionDbModel.tupled, UserReactionDbModel.unapply)
|
||||
override def * : ProvenShape[UserReactionDbModel] = (meetingId, userId, reactionEmoji, durationInSeconds, createdAt) <> (UserReactionDbModel.tupled, UserReactionDbModel.unapply)
|
||||
}
|
||||
|
||||
object UserReactionDAO {
|
||||
def insert(userId: String, reactionEmoji: String, durationInSeconds: Int) = {
|
||||
def insert(meetingId: String, userId: String, reactionEmoji: String, durationInSeconds: Int) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserReactionDbTableDef].forceInsert(
|
||||
UserReactionDbModel(
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
reactionEmoji = reactionEmoji,
|
||||
durationInSeconds = durationInSeconds,
|
||||
|
@ -7,34 +7,36 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class UserStateDbModel(
|
||||
userId: String,
|
||||
emoji: String = "none",
|
||||
away: Boolean = false,
|
||||
raiseHand: Boolean = false,
|
||||
guestStatus: String,
|
||||
guestStatusSetByModerator: Option[String],
|
||||
guestLobbyMessage: Option[String],
|
||||
mobile: Boolean,
|
||||
clientType: String,
|
||||
disconnected: Boolean = false,
|
||||
expired: Boolean = false,
|
||||
ejected: Boolean = false,
|
||||
ejectReason: Option[String],
|
||||
ejectReasonCode: Option[String],
|
||||
ejectedByModerator: Option[String],
|
||||
presenter: Boolean = false,
|
||||
pinned: Boolean = false,
|
||||
locked: Boolean = false,
|
||||
speechLocale: String,
|
||||
inactivityWarningDisplay: Boolean = false,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
emoji: String = "none",
|
||||
away: Boolean = false,
|
||||
raiseHand: Boolean = false,
|
||||
guestStatus: String,
|
||||
guestStatusSetByModerator: Option[String],
|
||||
guestLobbyMessage: Option[String],
|
||||
mobile: Boolean,
|
||||
clientType: String,
|
||||
disconnected: Boolean = false,
|
||||
expired: Boolean = false,
|
||||
ejected: Boolean = false,
|
||||
ejectReason: Option[String],
|
||||
ejectReasonCode: Option[String],
|
||||
ejectedByModerator: Option[String],
|
||||
presenter: Boolean = false,
|
||||
pinned: Boolean = false,
|
||||
locked: Boolean = false,
|
||||
speechLocale: String,
|
||||
inactivityWarningDisplay: Boolean = false,
|
||||
inactivityWarningTimeoutSecs: Option[Long],
|
||||
)
|
||||
|
||||
class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "user") {
|
||||
override def * = (
|
||||
userId,emoji,away,raiseHand,guestStatus,guestStatusSetByModerator,guestLobbyMessage,mobile,clientType,disconnected,
|
||||
meetingId, userId,emoji,away,raiseHand,guestStatus,guestStatusSetByModerator,guestLobbyMessage,mobile,clientType,disconnected,
|
||||
expired,ejected,ejectReason,ejectReasonCode,ejectedByModerator,presenter,pinned,locked,speechLocale,
|
||||
inactivityWarningDisplay, inactivityWarningTimeoutSecs) <> (UserStateDbModel.tupled, UserStateDbModel.unapply)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val emoji = column[String]("emoji")
|
||||
val away = column[Boolean]("away")
|
||||
@ -62,6 +64,7 @@ object UserStateDAO {
|
||||
def update(userState: UserState) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserStateDbTableDef]
|
||||
.filter(_.meetingId === userState.meetingId)
|
||||
.filter(_.userId === userState.intId)
|
||||
.map(u => (u.presenter, u.pinned, u.locked, u.speechLocale, u.emoji, u.away, u.raiseHand, u.mobile, u.clientType, u.disconnected))
|
||||
.update((userState.presenter, userState.pin, userState.locked, userState.speechLocale, userState.emoji, userState.away, userState.raiseHand, userState.mobile, userState.clientType, userState.userLeftFlag.left))
|
||||
@ -71,9 +74,10 @@ object UserStateDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateEjected(userId: String, ejectReason: String, ejectReasonCode: String, ejectedByModerator: String) = {
|
||||
def updateEjected(meetingId: String, userId: String, ejectReason: String, ejectReasonCode: String, ejectedByModerator: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserStateDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.ejected, u.ejectReason, u.ejectReasonCode, u.ejectedByModerator))
|
||||
.update((true, Some(ejectReason), Some(ejectReasonCode), Some(ejectedByModerator)))
|
||||
@ -83,10 +87,11 @@ object UserStateDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateExpired(intId: String, expired: Boolean) = {
|
||||
def updateExpired(meetingId: String, userId: String, expired: Boolean) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserStateDbTableDef]
|
||||
.filter(_.userId === intId)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.expired))
|
||||
.update((expired))
|
||||
).onComplete {
|
||||
@ -95,10 +100,11 @@ object UserStateDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateGuestStatus(intId: String, guestStatus: String, guestStatusSetByModerator: String) = {
|
||||
def updateGuestStatus(meetingId: String, userId: String, guestStatus: String, guestStatusSetByModerator: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserStateDbTableDef]
|
||||
.filter(_.userId === intId)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.guestStatus, u.guestStatusSetByModerator))
|
||||
.update((guestStatus,
|
||||
guestStatusSetByModerator match {
|
||||
@ -113,10 +119,11 @@ object UserStateDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateGuestLobbyMessage(intId: String, guestLobbyMessage: String) = {
|
||||
def updateGuestLobbyMessage(meetingId: String, userId: String, guestLobbyMessage: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserStateDbTableDef]
|
||||
.filter(_.userId === intId)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => u.guestLobbyMessage)
|
||||
.update(Some(guestLobbyMessage))
|
||||
).onComplete {
|
||||
@ -125,10 +132,11 @@ object UserStateDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def updateInactivityWarning(intId: String, inactivityWarningDisplay: Boolean, inactivityWarningTimeoutSecs: Long) = {
|
||||
def updateInactivityWarning(meetingId: String, userId: String, inactivityWarningDisplay: Boolean, inactivityWarningTimeoutSecs: Long) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserStateDbTableDef]
|
||||
.filter(_.userId === intId)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.inactivityWarningDisplay, u.inactivityWarningTimeoutSecs))
|
||||
.update((inactivityWarningDisplay,
|
||||
inactivityWarningTimeoutSecs match {
|
||||
|
@ -7,8 +7,8 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
case class UserTranscriptionErrorDbModel(
|
||||
userId: String,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
errorCode: String,
|
||||
errorMessage: String,
|
||||
lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
|
||||
@ -16,10 +16,10 @@ case class UserTranscriptionErrorDbModel(
|
||||
|
||||
class UserTranscriptionErrorDbTableDef(tag: Tag) extends Table[UserTranscriptionErrorDbModel](tag, None, "user_transcriptionError") {
|
||||
override def * = (
|
||||
userId, meetingId, errorCode, errorMessage, lastUpdatedAt
|
||||
meetingId, userId, errorCode, errorMessage, lastUpdatedAt
|
||||
) <> (UserTranscriptionErrorDbModel.tupled, UserTranscriptionErrorDbModel.unapply)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val meetingId = column[String]("meetingId")
|
||||
val errorCode = column[String]("errorCode")
|
||||
val errorMessage = column[String]("errorMessage")
|
||||
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
|
||||
@ -30,17 +30,15 @@ object UserTranscriptionErrorDAO {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserTranscriptionErrorDbTableDef].insertOrUpdate(
|
||||
UserTranscriptionErrorDbModel(
|
||||
userId = userId,
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
errorCode = errorCode,
|
||||
errorMessage = errorMessage,
|
||||
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
||||
)
|
||||
)
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => {
|
||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_transcriptionError table!")
|
||||
}
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_transcriptionError table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user_transcriptionError: $e")
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success }
|
||||
|
||||
case class UserVoiceConfStateDbModel(
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
voiceConf: String,
|
||||
voiceConfCallSession: String,
|
||||
@ -16,8 +17,9 @@ case class UserVoiceConfStateDbModel(
|
||||
|
||||
class UserVoiceConfStateDbTableDef(tag: Tag) extends Table[UserVoiceConfStateDbModel](tag, None, "user_voice") {
|
||||
override def * = (
|
||||
userId, voiceConf, voiceConfCallSession, voiceConfClientSession, voiceConfCallState
|
||||
meetingId, userId, voiceConf, voiceConfCallSession, voiceConfClientSession, voiceConfCallState
|
||||
) <> (UserVoiceConfStateDbModel.tupled, UserVoiceConfStateDbModel.unapply)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val voiceConf = column[String]("voiceConf")
|
||||
val voiceConfCallSession = column[String]("voiceConfCallSession")
|
||||
@ -26,10 +28,11 @@ class UserVoiceConfStateDbTableDef(tag: Tag) extends Table[UserVoiceConfStateDbM
|
||||
}
|
||||
|
||||
object UserVoiceConfStateDAO {
|
||||
def insertOrUpdate(userId: String, voiceConf: String, voiceConfCallSession: String, clientSession: String, callState: String) = {
|
||||
def insertOrUpdate(meetingId: String, userId: String, voiceConf: String, voiceConfCallSession: String, clientSession: String, callState: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserVoiceConfStateDbTableDef].insertOrUpdate(
|
||||
UserVoiceConfStateDbModel(
|
||||
meetingId = meetingId,
|
||||
userId = userId,
|
||||
voiceConf = voiceConf,
|
||||
voiceConfCallSession = voiceConfCallSession,
|
||||
@ -38,9 +41,7 @@ object UserVoiceConfStateDAO {
|
||||
)
|
||||
)
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => {
|
||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_voice table!")
|
||||
}
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_voice table!")
|
||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting voice: $e")
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success }
|
||||
|
||||
case class UserVoiceDbModel(
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
voiceUserId: String,
|
||||
callerName: String,
|
||||
@ -25,9 +26,10 @@ case class UserVoiceDbModel(
|
||||
|
||||
class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") {
|
||||
override def * = (
|
||||
userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly,
|
||||
meetingId, userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly,
|
||||
muted, spoke, talking, floor, lastFloorTime, startTime, endTime
|
||||
) <> (UserVoiceDbModel.tupled, UserVoiceDbModel.unapply)
|
||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||
val userId = column[String]("userId", O.PrimaryKey)
|
||||
val voiceUserId = column[String]("voiceUserId")
|
||||
val callerName = column[String]("callerName")
|
||||
@ -54,6 +56,7 @@ object UserVoiceDAO {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserVoiceDbTableDef].insertOrUpdate(
|
||||
UserVoiceDbModel(
|
||||
meetingId = voiceUserState.meetingId,
|
||||
userId = voiceUserState.intId,
|
||||
voiceUserId = voiceUserState.voiceUserId,
|
||||
callerName = voiceUserState.callerName,
|
||||
@ -99,13 +102,15 @@ object UserVoiceDAO {
|
||||
"spoke" = true,
|
||||
"endTime" = null,
|
||||
"startTime" = (case when "talking" is false then $now else "startTime" end)
|
||||
WHERE "userId" = ${voiceUserState.intId}"""
|
||||
WHERE "meetingId" = ${voiceUserState.meetingId}
|
||||
AND "userId" = ${voiceUserState.intId}"""
|
||||
} else {
|
||||
sqlu"""UPDATE user_voice SET
|
||||
"talking" = false,
|
||||
"startTime" = null,
|
||||
"endTime" = (case when "talking" is true then $now else "endTime" end)
|
||||
WHERE "userId" = ${voiceUserState.intId}"""
|
||||
WHERE "meetingId" = ${voiceUserState.meetingId}
|
||||
AND "userId" = ${voiceUserState.intId}"""
|
||||
}
|
||||
|
||||
DatabaseConnection.db.run(updateSql).onComplete {
|
||||
@ -114,10 +119,11 @@ object UserVoiceDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def delete(intId: String) = {
|
||||
def delete(meetingId: String, userId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserDbTableDef]
|
||||
.filter(_.userId === intId)
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.loggedOut))
|
||||
.update((true))
|
||||
).onComplete {
|
||||
@ -126,7 +132,7 @@ object UserVoiceDAO {
|
||||
}
|
||||
}
|
||||
|
||||
def deleteUserVoice(userId: String) = {
|
||||
def deleteUserVoice(meetingId: String,userId: String) = {
|
||||
//Meteor sets this props instead of removing
|
||||
// muted: false
|
||||
// talking: false
|
||||
@ -136,6 +142,7 @@ object UserVoiceDAO {
|
||||
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserVoiceDbTableDef]
|
||||
.filter(_.meetingId === meetingId)
|
||||
.filter(_.userId === userId)
|
||||
.map(u => (u.muted, u.talking, u.listenOnly, u.joined, u.spoke, u.startTime, u.endTime))
|
||||
.update((false, false, false, false, false, None, None))
|
||||
|
@ -1,60 +0,0 @@
|
||||
package org.bigbluebutton.core.db
|
||||
|
||||
import org.bigbluebutton.core.apps.whiteboard.Whiteboard
|
||||
import slick.jdbc.PostgresProfile.api._
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class UserWhiteboardDbModel(
|
||||
whiteboardId: String,
|
||||
userId: String,
|
||||
changedModeOn: Long,
|
||||
)
|
||||
|
||||
class UserWhiteboardDbTableDef(tag: Tag) extends Table[UserWhiteboardDbModel](tag, None, "user_whiteboard") {
|
||||
override def * = (
|
||||
whiteboardId, userId, changedModeOn) <> (UserWhiteboardDbModel.tupled, UserWhiteboardDbModel.unapply)
|
||||
def pk = primaryKey("user_whiteboard_pkey", (whiteboardId, userId))
|
||||
val whiteboardId = column[String]("whiteboardId")
|
||||
val userId = column[String]("userId")
|
||||
val changedModeOn = column[Long]("changedModeOn")
|
||||
}
|
||||
|
||||
object UserWhiteboardDAO {
|
||||
|
||||
def updateMultiuser(whiteboard: Whiteboard) = {
|
||||
|
||||
val deleteQuery = TableQuery[UserWhiteboardDbTableDef]
|
||||
.filter(_.whiteboardId === whiteboard.id)
|
||||
|
||||
if(whiteboard.multiUser.length > 0) {
|
||||
deleteQuery.filterNot(_.userId inSet whiteboard.multiUser)
|
||||
}
|
||||
|
||||
DatabaseConnection.db.run(deleteQuery.delete).onComplete {
|
||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Users deleted from Whiteboard ${whiteboard.id}")
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting users from whiteboard: $e")
|
||||
}
|
||||
|
||||
for {
|
||||
userId <- whiteboard.multiUser
|
||||
} yield {
|
||||
DatabaseConnection.db.run(
|
||||
TableQuery[UserWhiteboardDbTableDef].insertOrUpdate(
|
||||
UserWhiteboardDbModel(
|
||||
whiteboardId = whiteboard.id,
|
||||
userId = userId,
|
||||
changedModeOn = whiteboard.changedModeOn
|
||||
)
|
||||
)
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => {
|
||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_whiteboard table!")
|
||||
}
|
||||
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting user_whiteboard: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -45,6 +45,10 @@ object Pads {
|
||||
|
||||
def setGroupId(pads: Pads, externalId: String, groupId: String): Unit = pads.setGroupId(externalId, groupId)
|
||||
|
||||
def setPadId(pads: Pads, externalId: String, padId: String): Unit = pads.setGroupPadId(externalId, padId)
|
||||
|
||||
def setRev(pads: Pads, externalId: String, rev: Int): Unit = pads.setGroupRev(externalId, rev)
|
||||
|
||||
def getGroupById(pads: Pads, groupId: String): Option[PadGroup] = pads.getGroupById(groupId)
|
||||
}
|
||||
|
||||
@ -54,13 +58,31 @@ class Pads {
|
||||
def addGroup(externalId: String, model: String, name: String, userId: String): Unit = groups += externalId -> new PadGroup(externalId, model, name, userId)
|
||||
|
||||
def setGroupId(externalId: String, groupId: String): Unit = {
|
||||
groups.get(externalId) match {
|
||||
case Some(group) => groups += externalId -> new PadGroup(externalId, group.model, group.name, group.userId, groupId)
|
||||
case _ =>
|
||||
for {
|
||||
group <- groups.get(externalId)
|
||||
} yield {
|
||||
groups += externalId -> group.copy(groupId = groupId)
|
||||
}
|
||||
}
|
||||
|
||||
def setGroupPadId(externalId: String, padId: String): Unit = {
|
||||
for {
|
||||
group <- groups.get(externalId)
|
||||
} yield {
|
||||
groups += externalId -> group.copy(padId = padId)
|
||||
}
|
||||
}
|
||||
|
||||
def setGroupRev(externalId: String, rev: Int): Unit = {
|
||||
for {
|
||||
group <- groups.get(externalId)
|
||||
} yield {
|
||||
groups += externalId -> group.copy(rev = rev)
|
||||
}
|
||||
}
|
||||
|
||||
def getGroupById(groupId: String): Option[PadGroup] = groups.values.find(_.groupId == groupId)
|
||||
|
||||
}
|
||||
|
||||
class PadGroup(val externalId: String, val model: String, val name: String, val userId: String, val groupId: String = "")
|
||||
case class PadGroup(val externalId: String, val model: String, val name: String, val userId: String, val groupId: String = "", padId: String = "", rev: Int = 0)
|
||||
|
@ -114,7 +114,7 @@ object Polls {
|
||||
shape = pollResultToWhiteboardShape(result)
|
||||
annot <- send(result, shape)
|
||||
} yield {
|
||||
lm.wbModel.addAnnotations(annot.wbId, requesterId, Array[AnnotationVO](annot), false, false)
|
||||
lm.wbModel.addAnnotations(annot.wbId, lm.props.meetingProp.intId, requesterId, Array[AnnotationVO](annot), isPresenter = false, isModerator = false)
|
||||
showPollResult(pollId, lm.polls)
|
||||
(result, annot)
|
||||
}
|
||||
@ -150,7 +150,7 @@ object Polls {
|
||||
simplePoll <- getSimplePollResult(pollId, lm.polls)
|
||||
pvo <- handleRespondToPoll(simplePoll, requesterId, pollId, questionId, answerIds, lm)
|
||||
} yield {
|
||||
PollResponseDAO.insert(poll, requesterId, answerIds)
|
||||
PollResponseDAO.insert(poll, lm.props.meetingProp.intId, requesterId, answerIds)
|
||||
(pollId, pvo)
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,14 @@ import org.bigbluebutton.core.db.{UserBreakoutRoomDAO, UserDAO, UserDbModel}
|
||||
import org.bigbluebutton.core.domain.BreakoutRoom2x
|
||||
|
||||
object RegisteredUsers {
|
||||
def create(userId: String, extId: String, name: String, roles: String,
|
||||
def create(meetingId: String, userId: String, extId: String, name: String, roles: String,
|
||||
authToken: String, sessionToken: String, avatar: String, color: String, guest: Boolean, authenticated: Boolean,
|
||||
guestStatus: String, excludeFromDashboard: Boolean, enforceLayout: String,
|
||||
customParameters: Map[String, String], loggedOut: Boolean): RegisteredUser = {
|
||||
new RegisteredUser(
|
||||
userId,
|
||||
extId,
|
||||
meetingId,
|
||||
name,
|
||||
roles,
|
||||
authToken,
|
||||
@ -202,6 +203,7 @@ class RegisteredUsers {
|
||||
case class RegisteredUser(
|
||||
id: String,
|
||||
externId: String,
|
||||
meetingId: String,
|
||||
name: String,
|
||||
role: String,
|
||||
authToken: String,
|
||||
|
@ -49,7 +49,7 @@ object Users2x {
|
||||
val newUser = u.copy(userLeftFlag = UserLeftFlag(false, 0))
|
||||
users.save(newUser)
|
||||
UserStateDAO.update(newUser)
|
||||
UserStateDAO.updateExpired(u.intId, false)
|
||||
UserStateDAO.updateExpired(u.meetingId, u.intId, false)
|
||||
newUser
|
||||
}
|
||||
}
|
||||
@ -101,7 +101,7 @@ object Users2x {
|
||||
def resetLastInactivityInspect(users: Users2x, u: UserState): UserState = {
|
||||
val newUserState = modify(u)(_.lastInactivityInspect).setTo(0)
|
||||
users.save(newUserState)
|
||||
UserStateDAO.updateInactivityWarning(u.intId, inactivityWarningDisplay = false, 0)
|
||||
UserStateDAO.updateInactivityWarning(u.meetingId, u.intId, inactivityWarningDisplay = false, 0)
|
||||
newUserState
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ object Users2x {
|
||||
.modify(_.reactionChangedOn).setTo(System.currentTimeMillis())
|
||||
|
||||
users.save(newUser)
|
||||
UserReactionDAO.insert(intId, reactionEmoji, durationInSeconds)
|
||||
UserReactionDAO.insert(u.meetingId, u.intId, reactionEmoji, durationInSeconds)
|
||||
newUser
|
||||
}
|
||||
}
|
||||
@ -409,6 +409,7 @@ case class UserLeftFlag(left: Boolean, leftOn: Long)
|
||||
case class UserState(
|
||||
intId: String,
|
||||
extId: String,
|
||||
meetingId: String,
|
||||
name: String,
|
||||
role: String,
|
||||
guest: Boolean,
|
||||
|
@ -38,8 +38,8 @@ object VoiceUsers {
|
||||
UserVoiceDAO.insert(user)
|
||||
}
|
||||
|
||||
def removeWithIntId(users: VoiceUsers, intId: String): Option[VoiceUserState] = {
|
||||
UserVoiceDAO.deleteUserVoice(intId)
|
||||
def removeWithIntId(users: VoiceUsers, meetingId: String, intId: String): Option[VoiceUserState] = {
|
||||
UserVoiceDAO.deleteUserVoice(meetingId, intId)
|
||||
users.remove(intId)
|
||||
}
|
||||
|
||||
@ -197,6 +197,7 @@ case class VoiceUserVO2x(
|
||||
case class VoiceUserState(
|
||||
intId: String,
|
||||
voiceUserId: String,
|
||||
meetingId: String,
|
||||
callingWith: String,
|
||||
callerName: String,
|
||||
callerNum: String,
|
||||
|
@ -15,10 +15,10 @@ object Webcams {
|
||||
|
||||
def findAll(webcams: Webcams): Vector[WebcamStream] = webcams.toVector
|
||||
|
||||
def addWebcamStream(webcams: Webcams, webcam: WebcamStream): Option[WebcamStream] = {
|
||||
def addWebcamStream(meetingId: String, webcams: Webcams, webcam: WebcamStream): Option[WebcamStream] = {
|
||||
findWithStreamId(webcams, webcam.streamId) match {
|
||||
case None => {
|
||||
UserCameraDAO.insert(webcam)
|
||||
UserCameraDAO.insert(meetingId, webcam)
|
||||
Some(webcams.save(webcam))
|
||||
}
|
||||
case _ => None
|
||||
|
@ -3,9 +3,10 @@ package org.bigbluebutton.core.pubsub.senders
|
||||
import org.apache.pekko.actor.{ Actor, ActorLogging, Props }
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.common2.msgs.{ PluginDataChannelDeleteEntryMsgBody, _ }
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core2.ReceivedMessageRouter
|
||||
|
||||
import scala.reflect.runtime.universe._
|
||||
import org.bigbluebutton.common2.bus.ReceivedJsonMessage
|
||||
import org.bigbluebutton.common2.bus.IncomingJsonMessageBus
|
||||
@ -179,8 +180,6 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routePadMsg[PadPatchSysMsg](envelope, jsonNode)
|
||||
case PadUpdatePubMsg.NAME =>
|
||||
routeGenericMsg[PadUpdatePubMsg](envelope, jsonNode)
|
||||
case PadCapturePubMsg.NAME =>
|
||||
routePadMsg[PadCapturePubMsg](envelope, jsonNode)
|
||||
case PadPinnedReqMsg.NAME =>
|
||||
routeGenericMsg[PadPinnedReqMsg](envelope, jsonNode)
|
||||
|
||||
@ -420,11 +419,11 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeGenericMsg[CreateGroupChatReqMsg](envelope, jsonNode)
|
||||
|
||||
//Plugin
|
||||
case PluginDataChannelDispatchMessageMsg.NAME =>
|
||||
routeGenericMsg[PluginDataChannelDispatchMessageMsg](envelope, jsonNode)
|
||||
case PluginDataChannelPushEntryMsg.NAME =>
|
||||
routeGenericMsg[PluginDataChannelPushEntryMsg](envelope, jsonNode)
|
||||
|
||||
case PluginDataChannelDeleteMessageMsg.NAME =>
|
||||
routeGenericMsg[PluginDataChannelDeleteMessageMsg](envelope, jsonNode)
|
||||
case PluginDataChannelDeleteEntryMsg.NAME =>
|
||||
routeGenericMsg[PluginDataChannelDeleteEntryMsg](envelope, jsonNode)
|
||||
|
||||
case PluginDataChannelResetMsg.NAME =>
|
||||
routeGenericMsg[PluginDataChannelResetMsg](envelope, jsonNode)
|
||||
@ -470,7 +469,7 @@ class ReceivedJsonMsgHandlerActor(
|
||||
route[CheckGraphqlMiddlewareAlivePongSysMsg](meetingManagerChannel, envelope, jsonNode)
|
||||
|
||||
case _ =>
|
||||
log.error("Cannot route envelope name " + envelope.name)
|
||||
log.debug("Cannot route envelope name " + envelope.name)
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ trait HandlerHelpers extends SystemConfiguration {
|
||||
UserState(
|
||||
intId = regUser.id,
|
||||
extId = regUser.externId,
|
||||
meetingId = regUser.meetingId,
|
||||
name = regUser.name,
|
||||
role = regUser.role,
|
||||
guest = regUser.guest,
|
||||
@ -237,7 +238,7 @@ trait HandlerHelpers extends SystemConfiguration {
|
||||
} yield {
|
||||
model.rooms.values.foreach { room =>
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(liveMeeting.props.meetingProp.intId, room.id, reason)))
|
||||
UserBreakoutRoomDAO.updateLastBreakoutRoom(Vector(), room)
|
||||
UserBreakoutRoomDAO.updateLastBreakoutRoom(liveMeeting.props.meetingProp.intId, Vector(), room)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.apps.caption.CaptionApp2x
|
||||
import org.bigbluebutton.core.apps.chat.ChatApp2x
|
||||
import org.bigbluebutton.core.apps.externalvideo.ExternalVideoApp2x
|
||||
import org.bigbluebutton.core.apps.pads.PadsApp2x
|
||||
import org.bigbluebutton.core.apps.pads.{ PadsApp2x, PadslHdlrHelpers }
|
||||
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
|
||||
import org.bigbluebutton.core.apps.audiocaptions.AudioCaptionsApp2x
|
||||
import org.bigbluebutton.core.apps.timer.TimerApp2x
|
||||
@ -28,12 +28,13 @@ import org.bigbluebutton.core.models.{ Users2x, VoiceUsers, _ }
|
||||
import org.bigbluebutton.core2.{ MeetingStatus2x, Permissions }
|
||||
import org.bigbluebutton.core2.message.handlers._
|
||||
import org.bigbluebutton.core2.message.handlers.meeting._
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.common2.msgs.{ PluginDataChannelDeleteEntryMsgBody, _ }
|
||||
import org.bigbluebutton.core.apps.breakout._
|
||||
import org.bigbluebutton.core.apps.polls._
|
||||
import org.bigbluebutton.core.apps.voice._
|
||||
import org.apache.pekko.actor.Props
|
||||
import org.apache.pekko.actor.OneForOneStrategy
|
||||
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPathAsBooleanOrElse, getConfigPropertyValueByPathAsStringOrElse }
|
||||
import org.bigbluebutton.common2.msgs
|
||||
|
||||
import scala.concurrent.duration._
|
||||
@ -41,7 +42,7 @@ import org.bigbluebutton.core.apps.layout.LayoutApp2x
|
||||
import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr }
|
||||
import org.bigbluebutton.core.apps.plugin.PluginHdlrs
|
||||
import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr
|
||||
import org.bigbluebutton.core.db.{ NotificationDAO, UserStateDAO }
|
||||
import org.bigbluebutton.core.db.{ MeetingDAO, NotificationDAO, UserStateDAO }
|
||||
import org.bigbluebutton.core.models.VoiceUsers.{ findAllFreeswitchCallers, findAllListenOnlyVoiceUsers }
|
||||
import org.bigbluebutton.core.models.Webcams.findAll
|
||||
import org.bigbluebutton.core2.MeetingStatus2x.hasAuthedUserJoined
|
||||
@ -177,6 +178,9 @@ class MeetingActor(
|
||||
val msgEvent = MsgBuilder.buildMeetingCreatedEvtMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props)
|
||||
outGW.send(msgEvent)
|
||||
|
||||
//Insert meeting into the database
|
||||
MeetingDAO.insert(liveMeeting.props, liveMeeting.clientSettings)
|
||||
|
||||
// Create a default public group chat
|
||||
state = groupChatApp.handleCreateDefaultPublicGroupChat(state, liveMeeting, msgBus)
|
||||
|
||||
@ -204,6 +208,8 @@ class MeetingActor(
|
||||
|
||||
initLockSettings(liveMeeting, liveMeeting.props.lockSettingsProps)
|
||||
|
||||
initSharedNotes(liveMeeting)
|
||||
|
||||
/** *****************************************************************/
|
||||
// Helper to create fake users for testing (ralam jan 5, 2018)
|
||||
//object FakeTestData extends FakeTestData
|
||||
@ -294,7 +300,6 @@ class MeetingActor(
|
||||
case msg: SendBreakoutTimeRemainingInternalMsg =>
|
||||
handleSendBreakoutTimeRemainingInternalMsg(msg)
|
||||
case msg: CapturePresentationReqInternalMsg => presentationPodsApp.handle(msg, state, liveMeeting, msgBus)
|
||||
case msg: CaptureSharedNotesReqInternalMsg => presentationPodsApp.handle(msg, liveMeeting, msgBus)
|
||||
case msg: SendRecordingTimerInternalMsg =>
|
||||
state = usersApp.handleSendRecordingTimerInternalMsg(msg, state)
|
||||
|
||||
@ -319,6 +324,27 @@ class MeetingActor(
|
||||
MeetingStatus2x.setPermissions(liveMeeting.status, settings)
|
||||
}
|
||||
|
||||
private def initSharedNotes(liveMeeting: LiveMeeting): Unit = {
|
||||
val sharedNotesEnabledInClientSettings = getConfigPropertyValueByPathAsBooleanOrElse(
|
||||
liveMeeting.clientSettings,
|
||||
"public.notes.enabled",
|
||||
alternativeValue = true
|
||||
)
|
||||
|
||||
if (sharedNotesEnabledInClientSettings && !liveMeeting.props.meetingProp.disabledFeatures.contains("sharedNotes")) {
|
||||
val sharedNotesPadId = getConfigPropertyValueByPathAsStringOrElse(
|
||||
liveMeeting.clientSettings,
|
||||
"public.notes.id",
|
||||
alternativeValue = ""
|
||||
)
|
||||
|
||||
if (!Pads.hasGroup(liveMeeting.pads, sharedNotesPadId)) {
|
||||
Pads.addGroup(liveMeeting.pads, sharedNotesPadId, sharedNotesPadId, sharedNotesPadId, "SYSTEM")
|
||||
PadslHdlrHelpers.broadcastPadCreateGroupCmdMsg(outGW, liveMeeting.props.meetingProp.intId, sharedNotesPadId, sharedNotesPadId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def updateVoiceUserLastActivity(userId: String) {
|
||||
for {
|
||||
vu <- VoiceUsers.findWithVoiceUserId(liveMeeting.voiceUsers, userId)
|
||||
@ -530,7 +556,6 @@ class MeetingActor(
|
||||
case m: MakePresentationDownloadReqMsg => presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: NewPresFileAvailableMsg => presentationPodsApp.handle(m, liveMeeting, msgBus)
|
||||
case m: PresAnnStatusMsg => presentationPodsApp.handle(m, liveMeeting, msgBus)
|
||||
case m: PadCapturePubMsg => presentationPodsApp.handle(m, liveMeeting, msgBus)
|
||||
|
||||
// Presentation Pods
|
||||
case m: CreateNewPresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
@ -603,8 +628,8 @@ class MeetingActor(
|
||||
updateUserLastActivity(m.body.msg.sender.id)
|
||||
|
||||
// Plugin
|
||||
case m: PluginDataChannelDispatchMessageMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||
case m: PluginDataChannelDeleteMessageMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||
case m: PluginDataChannelPushEntryMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||
case m: PluginDataChannelDeleteEntryMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||
case m: PluginDataChannelResetMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||
|
||||
// Webcams
|
||||
@ -934,7 +959,7 @@ class MeetingActor(
|
||||
Polls.handleStopPollReqMsg(state, u.intId, liveMeeting)
|
||||
}
|
||||
|
||||
UserStateDAO.updateExpired(u.intId, true)
|
||||
UserStateDAO.updateExpired(u.meetingId, u.intId, true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -985,7 +1010,7 @@ class MeetingActor(
|
||||
|
||||
val secsToDisconnect = TimeUnit.MILLISECONDS.toSeconds(expiryTracker.userActivitySignResponseDelayInMs);
|
||||
Sender.sendUserInactivityInspectMsg(liveMeeting.props.meetingProp.intId, u.intId, secsToDisconnect, outGW)
|
||||
UserStateDAO.updateInactivityWarning(u.intId, inactivityWarningDisplay = true, secsToDisconnect)
|
||||
UserStateDAO.updateInactivityWarning(u.meetingId, u.intId, inactivityWarningDisplay = true, secsToDisconnect)
|
||||
updateUserLastInactivityInspect(u.intId)
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@ package org.bigbluebutton.core.running
|
||||
|
||||
import org.apache.pekko.actor.ActorContext
|
||||
import org.bigbluebutton.ClientSettings
|
||||
import org.bigbluebutton.ClientSettings.{getConfigPropertyValueByPathAsBooleanOrElse, getConfigPropertyValueByPathAsStringOrElse}
|
||||
import org.bigbluebutton.common2.domain.DefaultProps
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.apps.pads.PadslHdlrHelpers
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
|
||||
object RunningMeeting {
|
||||
|
@ -206,7 +206,6 @@ class AnalyticsActor(val includeChat: Boolean) extends Actor with ActorLogging {
|
||||
case m: PadUpdatedEvtMsg => logMessage(msg)
|
||||
case m: PadUpdatePubMsg => logMessage(msg)
|
||||
case m: PadUpdateCmdMsg => logMessage(msg)
|
||||
case m: PadCapturePubMsg => logMessage(msg)
|
||||
|
||||
case _ => // ignore message
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ object RunningMeetings {
|
||||
|
||||
def add(meetings: RunningMeetings, meeting: RunningMeeting): RunningMeeting = {
|
||||
meetings.save(meeting)
|
||||
MeetingDAO.insert(meeting.props, meeting.clientSettings)
|
||||
meeting
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ trait SetPrivateGuestLobbyMessageCmdMsgHdlr extends RightsManagementTrait {
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
} else {
|
||||
GuestsWaiting.setPrivateGuestLobbyMessage(liveMeeting.guestsWaiting, msg.body.guestId, msg.body.message)
|
||||
UserStateDAO.updateGuestLobbyMessage(msg.body.guestId, msg.body.message)
|
||||
UserStateDAO.updateGuestLobbyMessage(msg.header.meetingId, msg.body.guestId, msg.body.message)
|
||||
val event = MsgBuilder.buildPrivateGuestLobbyMsgChangedEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
msg.header.userId,
|
||||
|
@ -29,16 +29,18 @@ trait FakeTestData {
|
||||
val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", "#ff6242", guest2.authed, System.currentTimeMillis())
|
||||
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2)
|
||||
|
||||
val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
|
||||
val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(meetingId, CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, vu1)
|
||||
|
||||
val vu2 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
val vu2 = FakeUserGenerator.createFakeVoiceOnlyUser(meetingId, CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, vu2)
|
||||
val vu3 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
val vu3 = FakeUserGenerator.createFakeVoiceOnlyUser(meetingId, CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, vu3)
|
||||
val vu4 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
val vu4 = FakeUserGenerator.createFakeVoiceOnlyUser(meetingId, CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, vu4)
|
||||
val vu5 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
val vu5 = FakeUserGenerator.createFakeVoiceOnlyUser(meetingId, CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, vu5)
|
||||
|
||||
for (i <- 1 to 50) {
|
||||
@ -60,13 +62,14 @@ trait FakeTestData {
|
||||
val others = rusers.filterNot(u => u.intId == ruser1.id)
|
||||
val subscribers = others.map { o => o.intId }
|
||||
val wstream1 = FakeUserGenerator.createFakeWebcamStreamFor(ruser1.id, subscribers.toSet)
|
||||
Webcams.addWebcamStream(liveMeeting.webcams, wstream1)
|
||||
Webcams.addWebcamStream(liveMeeting.props.meetingProp.intId, liveMeeting.webcams, wstream1)
|
||||
|
||||
createFakeUser(liveMeeting, ruser1)
|
||||
}
|
||||
|
||||
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
|
||||
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false,
|
||||
UserState(intId = regUser.id, extId = regUser.externId, meetingId = regUser.meetingId,
|
||||
name = regUser.name, role = regUser.role, pin = false,
|
||||
mobile = false, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, locked = false, presenter = false,
|
||||
avatar = regUser.avatarURL, color = "#ff6242", clientType = "unknown", userLeftFlag = UserLeftFlag(false, 0))
|
||||
|
@ -55,7 +55,7 @@ object FakeUserGenerator {
|
||||
RandomStringGenerator.randomAlphanumericString(10) + ".png"
|
||||
val color = "#ff6242"
|
||||
|
||||
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
||||
val ru = RegisteredUsers.create(meetingId, userId = id, extId, name, role,
|
||||
authToken, sessionToken, avatarURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, "", Map(), false)
|
||||
RegisteredUsers.add(users, ru, meetingId)
|
||||
ru
|
||||
@ -65,22 +65,50 @@ object FakeUserGenerator {
|
||||
listenOnly: Boolean, floor: Boolean = false): VoiceUserState = {
|
||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||
val lastFloorTime = System.currentTimeMillis().toString();
|
||||
VoiceUserState(intId = user.id, voiceUserId = voiceUserId, callingWith, callerName = user.name,
|
||||
callerNum = user.name, "#ff6242", muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime,
|
||||
VoiceUserState(
|
||||
intId = user.id,
|
||||
voiceUserId = voiceUserId,
|
||||
meetingId = user.meetingId,
|
||||
callingWith,
|
||||
callerName = user.name,
|
||||
callerNum = user.name,
|
||||
"#ff6242",
|
||||
muted,
|
||||
talking,
|
||||
listenOnly,
|
||||
"freeswitch",
|
||||
System.currentTimeMillis(),
|
||||
floor,
|
||||
lastFloorTime,
|
||||
false,
|
||||
"9b3f4504-275d-4315-9922-21174262d88c")
|
||||
"9b3f4504-275d-4315-9922-21174262d88c"
|
||||
)
|
||||
}
|
||||
|
||||
def createFakeVoiceOnlyUser(callingWith: String, muted: Boolean, talking: Boolean,
|
||||
def createFakeVoiceOnlyUser(meetingId: String, callingWith: String, muted: Boolean, talking: Boolean,
|
||||
listenOnly: Boolean, floor: Boolean = false): VoiceUserState = {
|
||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||
val intId = "v_" + RandomStringGenerator.randomAlphanumericString(16)
|
||||
val name = getRandomElement(firstNames, random) + " " + getRandomElement(lastNames, random)
|
||||
val lastFloorTime = System.currentTimeMillis().toString();
|
||||
VoiceUserState(intId, voiceUserId = voiceUserId, callingWith, callerName = name,
|
||||
callerNum = name, "#ff6242", muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime,
|
||||
false,
|
||||
"9b3f4504-275d-4315-9922-21174262d88c")
|
||||
VoiceUserState(
|
||||
intId,
|
||||
voiceUserId = voiceUserId,
|
||||
meetingId = "",
|
||||
callingWith,
|
||||
callerName = name,
|
||||
callerNum = name,
|
||||
"#ff6242",
|
||||
muted,
|
||||
talking,
|
||||
listenOnly,
|
||||
"freeswitch",
|
||||
System.currentTimeMillis(),
|
||||
floor,
|
||||
lastFloorTime,
|
||||
hold = false,
|
||||
"9b3f4504-275d-4315-9922-21174262d88c"
|
||||
)
|
||||
}
|
||||
|
||||
def createFakeWebcamStreamFor(userId: String, subscribers: Set[String]): WebcamStream = {
|
||||
|
@ -5,7 +5,7 @@ import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core.util.RandomStringGenerator
|
||||
|
||||
object TestDataGen {
|
||||
def createRegisteredUser(users: RegisteredUsers, name: String, role: String,
|
||||
def createRegisteredUser(meetingId: String, users: RegisteredUsers, name: String, role: String,
|
||||
guest: Boolean, authed: Boolean, waitForApproval: Boolean): RegisteredUser = {
|
||||
val id = "w_" + RandomStringGenerator.randomAlphanumericString(16)
|
||||
val extId = RandomStringGenerator.randomAlphanumericString(16)
|
||||
@ -15,7 +15,7 @@ object TestDataGen {
|
||||
RandomStringGenerator.randomAlphanumericString(10) + ".png"
|
||||
val color = "#ff6242"
|
||||
|
||||
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
||||
val ru = RegisteredUsers.create(meetingId, userId = id, extId, name, role,
|
||||
authToken, sessionToken, avatarURL, color, guest, authed, GuestStatus.ALLOW, false, "", Map(), false)
|
||||
|
||||
RegisteredUsers.add(users, ru, meetingId = "test")
|
||||
@ -25,19 +25,45 @@ object TestDataGen {
|
||||
def createVoiceUserForUser(user: RegisteredUser, callingWith: String, muted: Boolean, talking: Boolean,
|
||||
listenOnly: Boolean): VoiceUserState = {
|
||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||
VoiceUserState(intId = user.id, voiceUserId = voiceUserId, callingWith, callerName = user.name,
|
||||
callerNum = user.name, "#ff6242", muted, talking, listenOnly,
|
||||
false,
|
||||
VoiceUserState(
|
||||
intId = user.id,
|
||||
voiceUserId = voiceUserId,
|
||||
meetingId = user.meetingId,
|
||||
callingWith,
|
||||
callerName = user.name,
|
||||
callerNum = user.name,
|
||||
color = "#ff6242",
|
||||
muted,
|
||||
talking,
|
||||
listenOnly,
|
||||
calledInto = "freeswitch",
|
||||
lastStatusUpdateOn = System.currentTimeMillis(),
|
||||
floor = false,
|
||||
lastFloorTime = System.currentTimeMillis().toString(),
|
||||
hold = false,
|
||||
"9b3f4504-275d-4315-9922-21174262d88c")
|
||||
}
|
||||
|
||||
def createFakeVoiceOnlyUser(callingWith: String, muted: Boolean, talking: Boolean,
|
||||
def createFakeVoiceOnlyUser(meetingId: String, callingWith: String, muted: Boolean, talking: Boolean,
|
||||
listenOnly: Boolean, name: String): VoiceUserState = {
|
||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||
val intId = "v_" + RandomStringGenerator.randomAlphanumericString(16)
|
||||
VoiceUserState(intId, voiceUserId = voiceUserId, callingWith, callerName = name,
|
||||
callerNum = name, "#ff6242", muted, talking, listenOnly
|
||||
false,
|
||||
VoiceUserState(
|
||||
intId,
|
||||
voiceUserId = voiceUserId,
|
||||
meetingId,
|
||||
callingWith,
|
||||
callerName = name,
|
||||
callerNum = name,
|
||||
color = "#ff6242",
|
||||
muted,
|
||||
talking,
|
||||
listenOnly,
|
||||
calledInto = "freeswitch",
|
||||
lastStatusUpdateOn = System.currentTimeMillis(),
|
||||
floor = false,
|
||||
lastFloorTime = System.currentTimeMillis().toString(),
|
||||
hold = false,
|
||||
"9b3f4504-275d-4315-9922-21174262d88c")
|
||||
}
|
||||
|
||||
@ -47,8 +73,8 @@ object TestDataGen {
|
||||
}
|
||||
|
||||
def createUserFor(liveMeeting: LiveMeeting, regUser: RegisteredUser, presenter: Boolean): UserState = {
|
||||
val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
||||
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||
val u = UserState(intId = regUser.id, extId = regUser.externId, meetingId = regUser.meetingId, name = regUser.name,
|
||||
role = regUser.role, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, pin = false, mobile = false,
|
||||
locked = false, presenter = false, avatar = regUser.avatarURL, color = "#ff6242",
|
||||
clientType = "unknown", userLeftFlag = UserLeftFlag(false, 0))
|
||||
|
@ -69,6 +69,7 @@ case class LockSettingsProps(
|
||||
|
||||
case class SystemProps(
|
||||
html5InstanceId: Int,
|
||||
loginUrl: String,
|
||||
logoutUrl: String,
|
||||
customLogoURL: String,
|
||||
bannerText: String,
|
||||
|
@ -5,22 +5,24 @@ package org.bigbluebutton.common2.msgs
|
||||
/**
|
||||
* Sent from graphql-actions to bbb-akka
|
||||
*/
|
||||
object PluginDataChannelDispatchMessageMsg { val NAME = "PluginDataChannelDispatchMessageMsg" }
|
||||
case class PluginDataChannelDispatchMessageMsg(header: BbbClientMsgHeader, body: PluginDataChannelDispatchMessageMsgBody) extends StandardMsg
|
||||
case class PluginDataChannelDispatchMessageMsgBody(
|
||||
object PluginDataChannelPushEntryMsg { val NAME = "PluginDataChannelPushEntryMsg" }
|
||||
case class PluginDataChannelPushEntryMsg(header: BbbClientMsgHeader, body: PluginDataChannelPushEntryMsgBody) extends StandardMsg
|
||||
case class PluginDataChannelPushEntryMsgBody(
|
||||
pluginName: String,
|
||||
dataChannel: String,
|
||||
channelName: String,
|
||||
subChannelName: String,
|
||||
payloadJson: String,
|
||||
toRoles: List[String],
|
||||
toUserIds: List[String],
|
||||
)
|
||||
|
||||
object PluginDataChannelDeleteMessageMsg { val NAME = "PluginDataChannelDeleteMessageMsg" }
|
||||
case class PluginDataChannelDeleteMessageMsg(header: BbbClientMsgHeader, body: PluginDataChannelDeleteMessageMsgBody) extends StandardMsg
|
||||
case class PluginDataChannelDeleteMessageMsgBody(
|
||||
object PluginDataChannelDeleteEntryMsg { val NAME = "PluginDataChannelDeleteEntryMsg" }
|
||||
case class PluginDataChannelDeleteEntryMsg(header: BbbClientMsgHeader, body: PluginDataChannelDeleteEntryMsgBody) extends StandardMsg
|
||||
case class PluginDataChannelDeleteEntryMsgBody(
|
||||
pluginName: String,
|
||||
dataChannel: String,
|
||||
messageId: String
|
||||
subChannelName: String,
|
||||
channelName: String,
|
||||
entryId: String
|
||||
)
|
||||
|
||||
|
||||
@ -28,5 +30,6 @@ object PluginDataChannelResetMsg { val NAME = "PluginDataChannelResetMsg" }
|
||||
case class PluginDataChannelResetMsg(header: BbbClientMsgHeader, body: PluginDataChannelResetMsgBody) extends StandardMsg
|
||||
case class PluginDataChannelResetMsgBody(
|
||||
pluginName: String,
|
||||
dataChannel: String
|
||||
subChannelName: String,
|
||||
channelName: String
|
||||
)
|
||||
|
@ -37,6 +37,7 @@ public class ApiParams {
|
||||
public static final String IS_BREAKOUT = "isBreakout";
|
||||
public static final String LOGO = "logo";
|
||||
public static final String LOGOUT_TIMER = "logoutTimer";
|
||||
public static final String LOGIN_URL = "loginURL";
|
||||
public static final String LOGOUT_URL = "logoutURL";
|
||||
public static final String MAX_PARTICIPANTS = "maxParticipants";
|
||||
public static final String MEETING_ID = "meetingID";
|
||||
|
@ -452,7 +452,7 @@ public class MeetingService implements MessageListener {
|
||||
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
|
||||
m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(),
|
||||
m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getAllowModsToEjectCameras(), m.getMeetingKeepEvents(),
|
||||
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(), m.getLogoutUrl(), m.getCustomLogoURL(),
|
||||
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(), m.getLoginUrl(), m.getLogoutUrl(), m.getCustomLogoURL(),
|
||||
m.getBannerText(), m.getBannerColor(), m.getGroups(), m.getDisabledFeatures(), m.getNotifyRecordingIsOn(),
|
||||
m.getPresentationUploadExternalDescription(), m.getPresentationUploadExternalUrl(),
|
||||
m.getOverrideClientSettings());
|
||||
|
@ -454,6 +454,7 @@ public class ParamsProcessorUtil {
|
||||
// Get all the other relevant parameters and generate defaults if none
|
||||
// has been provided.
|
||||
String dialNumber = processDialNumber(params.get(ApiParams.DIAL_NUMBER));
|
||||
String loginUrl = params.get(ApiParams.LOGIN_URL);
|
||||
String logoutUrl = processLogoutUrl(params.get(ApiParams.LOGOUT_URL));
|
||||
boolean record = processRecordMeeting(params.get(ApiParams.RECORD));
|
||||
int maxUsers = processMaxUser(params.get(ApiParams.MAX_PARTICIPANTS));
|
||||
@ -742,7 +743,9 @@ public class ParamsProcessorUtil {
|
||||
internalMeetingId, createTime).withName(meetingName)
|
||||
.withMaxUsers(maxUsers).withModeratorPass(modPass)
|
||||
.withViewerPass(viewerPass).withRecording(record)
|
||||
.withDuration(meetingDuration).withLogoutUrl(logoutUrl)
|
||||
.withDuration(meetingDuration)
|
||||
.withLoginUrl(loginUrl)
|
||||
.withLogoutUrl(logoutUrl)
|
||||
.withLogoutTimer(logoutTimer)
|
||||
.withBannerText(bannerText).withBannerColor(bannerColor)
|
||||
.withTelVoice(telVoice).withWebVoice(webVoice)
|
||||
|
@ -60,6 +60,7 @@ public class Meeting {
|
||||
private String welcomeMsgTemplate;
|
||||
private String welcomeMsg;
|
||||
private String modOnlyMessage = "";
|
||||
private String loginUrl;
|
||||
private String logoutUrl;
|
||||
private int logoutTimer = 0;
|
||||
private int maxUsers;
|
||||
@ -143,6 +144,7 @@ public class Meeting {
|
||||
maxUsers = builder.maxUsers;
|
||||
bannerColor = builder.bannerColor;
|
||||
bannerText = builder.bannerText;
|
||||
loginUrl = builder.loginUrl;
|
||||
logoutUrl = builder.logoutUrl;
|
||||
logoutTimer = builder.logoutTimer;
|
||||
defaultAvatarURL = builder.defaultAvatarURL;
|
||||
@ -560,6 +562,10 @@ public class Meeting {
|
||||
}
|
||||
|
||||
|
||||
public String getLoginUrl() {
|
||||
return loginUrl;
|
||||
}
|
||||
|
||||
public String getLogoutUrl() {
|
||||
return logoutUrl;
|
||||
}
|
||||
@ -899,6 +905,7 @@ public class Meeting {
|
||||
private String telVoice;
|
||||
private String welcomeMsgTemplate;
|
||||
private String welcomeMsg;
|
||||
private String loginUrl;
|
||||
private String logoutUrl;
|
||||
private String bannerColor;
|
||||
private String bannerText;
|
||||
@ -1057,9 +1064,14 @@ public class Meeting {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withLoginUrl(String l) {
|
||||
loginUrl = l;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withLogoutUrl(String l) {
|
||||
logoutUrl = l;
|
||||
return this;
|
||||
logoutUrl = l;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withLogoutTimer(int l) {
|
||||
|
@ -42,6 +42,7 @@ public interface IBbbWebApiGWApp {
|
||||
BreakoutRoomsParams breakoutParams,
|
||||
LockSettingsParams lockSettingsParams,
|
||||
Integer html5InstanceId,
|
||||
String loginUrl,
|
||||
String logoutUrl,
|
||||
String customLogoURL,
|
||||
String bannerText,
|
||||
|
@ -149,6 +149,7 @@ class BbbWebApiGWApp(
|
||||
breakoutParams: BreakoutRoomsParams,
|
||||
lockSettingsParams: LockSettingsParams,
|
||||
html5InstanceId: java.lang.Integer,
|
||||
loginUrl: String,
|
||||
logoutUrl: String,
|
||||
customLogoURL: String,
|
||||
bannerText: String,
|
||||
@ -235,6 +236,10 @@ class BbbWebApiGWApp(
|
||||
|
||||
val systemProps = SystemProps(
|
||||
html5InstanceId,
|
||||
loginUrl match {
|
||||
case url: String => url
|
||||
case _ => ""
|
||||
},
|
||||
logoutUrl,
|
||||
customLogoURL,
|
||||
bannerText match {
|
||||
|
24
bbb-graphql-actions/src/actions/captionSetOwner.ts
Normal file
24
bbb-graphql-actions/src/actions/captionSetOwner.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { RedisMessage } from '../types';
|
||||
|
||||
export default function buildRedisMessage(sessionVariables: Record<string, unknown>, input: Record<string, unknown>): RedisMessage {
|
||||
const eventName = `UpdateCaptionOwnerPubMsg`;
|
||||
|
||||
const routing = {
|
||||
meetingId: sessionVariables['x-hasura-meetingid'] as String,
|
||||
userId: sessionVariables['x-hasura-userid'] as String
|
||||
};
|
||||
|
||||
const header = {
|
||||
name: eventName,
|
||||
meetingId: routing.meetingId,
|
||||
userId: routing.userId
|
||||
};
|
||||
|
||||
const body = {
|
||||
name: '',
|
||||
locale: input.locale,
|
||||
ownerId: input.ownerUserId,
|
||||
};
|
||||
|
||||
return { eventName, routing, header, body };
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { RedisMessage } from '../types';
|
||||
import { ValidationError } from '../types/ValidationError';
|
||||
|
||||
export default function buildRedisMessage(sessionVariables: Record<string, unknown>, input: Record<string, unknown>): RedisMessage {
|
||||
const eventName = `PluginDataChannelDeleteMessageMsg`;
|
||||
@ -17,8 +16,9 @@ export default function buildRedisMessage(sessionVariables: Record<string, unkno
|
||||
|
||||
const body = {
|
||||
pluginName: input.pluginName,
|
||||
dataChannel: input.dataChannel,
|
||||
messageId: input.messageId
|
||||
channelName: input.channelName,
|
||||
subChannelName: input.subChannelName,
|
||||
entryId: input.entryId
|
||||
};
|
||||
|
||||
return { eventName, routing, header, body };
|
@ -2,7 +2,7 @@ import { RedisMessage } from '../types';
|
||||
import { ValidationError } from '../types/ValidationError';
|
||||
|
||||
export default function buildRedisMessage(sessionVariables: Record<string, unknown>, input: Record<string, unknown>): RedisMessage {
|
||||
const eventName = `PluginDataChannelDispatchMessageMsg`;
|
||||
const eventName = `PluginDataChannelPushEntryMsg`;
|
||||
|
||||
const routing = {
|
||||
meetingId: sessionVariables['x-hasura-meetingid'] as String,
|
||||
@ -23,7 +23,8 @@ export default function buildRedisMessage(sessionVariables: Record<string, unkno
|
||||
|
||||
const body = {
|
||||
pluginName: input.pluginName,
|
||||
dataChannel: input.dataChannel,
|
||||
channelName: input.channelName,
|
||||
subChannelName: input.subChannelName,
|
||||
payloadJson: input.payloadJson,
|
||||
toRoles: input.toRoles,
|
||||
toUserIds: input.toUserIds
|
@ -1,5 +1,4 @@
|
||||
import { RedisMessage } from '../types';
|
||||
import { ValidationError } from '../types/ValidationError';
|
||||
|
||||
export default function buildRedisMessage(sessionVariables: Record<string, unknown>, input: Record<string, unknown>): RedisMessage {
|
||||
const eventName = `PluginDataChannelResetMsg`;
|
||||
@ -17,7 +16,8 @@ export default function buildRedisMessage(sessionVariables: Record<string, unkno
|
||||
|
||||
const body = {
|
||||
pluginName: input.pluginName,
|
||||
dataChannel: input.dataChannel
|
||||
channelName: input.channelName,
|
||||
subChannelName: input.subChannelName
|
||||
};
|
||||
|
||||
return { eventName, routing, header, body };
|
||||
|
@ -20,6 +20,7 @@ create table "meeting" (
|
||||
"presentationUploadExternalUrl" varchar(500),
|
||||
"learningDashboardAccessToken" varchar(100),
|
||||
"html5InstanceId" varchar(100),
|
||||
"loginUrl" varchar(500),
|
||||
"logoutUrl" varchar(500),
|
||||
"customLogoUrl" varchar(500),
|
||||
"bannerText" text,
|
||||
@ -248,9 +249,9 @@ create view "v_meeting_group" as select * from meeting_group;
|
||||
-- ========== User tables
|
||||
|
||||
CREATE TABLE "user" (
|
||||
"userId" varchar(50) NOT NULL PRIMARY KEY,
|
||||
"meetingId" varchar(100) references "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"userId" varchar(50) NOT NULL,
|
||||
"extId" varchar(50),
|
||||
"meetingId" varchar(100) references "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"name" varchar(255),
|
||||
"role" varchar(20),
|
||||
"avatar" varchar(500),
|
||||
@ -275,10 +276,11 @@ CREATE TABLE "user" (
|
||||
"awayTime" timestamp with time zone,
|
||||
"emoji" varchar,
|
||||
"emojiTime" timestamp with time zone,
|
||||
"guestStatusSetByModerator" varchar(50) references "user"("userId") ON DELETE SET NULL,
|
||||
"guestStatusSetByModerator" varchar(50),
|
||||
"guestLobbyMessage" text,
|
||||
"mobile" bool,
|
||||
"clientType" varchar(50),
|
||||
"transferredFromParentMeeting" bool default false, --when a user join in breakoutRoom only in audio
|
||||
"disconnected" bool default false, -- this is the old leftFlag (that was renamed), set when the user just closed the client
|
||||
"expired" bool default false, -- when it is been some time the user is disconnected
|
||||
"ejected" bool,
|
||||
@ -292,7 +294,9 @@ CREATE TABLE "user" (
|
||||
"inactivityWarningDisplay" bool default FALSE,
|
||||
"inactivityWarningTimeoutSecs" numeric,
|
||||
"hasDrawPermissionOnCurrentPage" bool default FALSE,
|
||||
"echoTestRunningAt" timestamp with time zone
|
||||
"echoTestRunningAt" timestamp with time zone,
|
||||
CONSTRAINT "user_pkey" PRIMARY KEY ("meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "guestStatusSetByModerator") REFERENCES "user"("meetingId","userId") ON DELETE SET NULL
|
||||
);
|
||||
CREATE INDEX "idx_user_meetingId" ON "user"("meetingId");
|
||||
CREATE INDEX "idx_user_extId" ON "user"("meetingId", "extId");
|
||||
@ -478,9 +482,10 @@ where u."guestStatus" = 'WAIT';
|
||||
--it is necessary because v_user has some conditions like "lockSettings-hideUserList"
|
||||
--but viewers still needs to query this users as foreign key of chat, cameras, etc
|
||||
CREATE OR REPLACE VIEW "v_user_ref"
|
||||
AS SELECT "user"."userId",
|
||||
"user"."extId",
|
||||
AS SELECT
|
||||
"user"."meetingId",
|
||||
"user"."userId",
|
||||
"user"."extId",
|
||||
"user"."name",
|
||||
"user"."nameSortable",
|
||||
"user"."avatar",
|
||||
@ -512,16 +517,17 @@ AS SELECT "user"."userId",
|
||||
FROM "user";
|
||||
|
||||
create table "user_customParameter"(
|
||||
"userId" varchar(50) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"parameter" varchar(255),
|
||||
"value" varchar(255),
|
||||
CONSTRAINT "user_customParameter_pkey" PRIMARY KEY ("userId","parameter")
|
||||
CONSTRAINT "user_customParameter_pkey" PRIMARY KEY ("meetingId", "userId","parameter"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE VIEW "v_user_customParameter" AS
|
||||
SELECT u."meetingId", "user_customParameter".*
|
||||
FROM "user_customParameter"
|
||||
JOIN "user" u ON u."userId" = "user_customParameter"."userId";
|
||||
SELECT *
|
||||
FROM "user_customParameter";
|
||||
|
||||
CREATE VIEW "v_user_welcomeMsgs" AS
|
||||
SELECT
|
||||
@ -534,7 +540,8 @@ join meeting_welcome w USING("meetingId");
|
||||
|
||||
|
||||
CREATE TABLE "user_voice" (
|
||||
"userId" varchar(50) PRIMARY KEY NOT NULL REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"voiceUserId" varchar(100),
|
||||
"callerName" varchar(100),
|
||||
"callerNum" varchar(100),
|
||||
@ -551,7 +558,9 @@ CREATE TABLE "user_voice" (
|
||||
"voiceConfClientSession" varchar(10),
|
||||
"voiceConfCallState" varchar(30),
|
||||
"endTime" bigint,
|
||||
"startTime" bigint
|
||||
"startTime" bigint,
|
||||
CONSTRAINT "user_voice_pkey" PRIMARY KEY ("meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
--CREATE INDEX "idx_user_voice_userId" ON "user_voice"("userId");
|
||||
-- + 6000 means it will hide after 6 seconds
|
||||
@ -564,19 +573,26 @@ GENERATED ALWAYS AS (to_timestamp("startTime"::double precision / 1000)) STORED;
|
||||
ALTER TABLE "user_voice" ADD COLUMN "endedAt" timestamp with time zone
|
||||
GENERATED ALWAYS AS (to_timestamp("endTime"::double precision / 1000)) STORED;
|
||||
|
||||
CREATE INDEX "idx_user_voice_userId_talking" ON "user_voice"("userId","talking");
|
||||
CREATE INDEX "idx_user_voice_userId_hideTalkingIndicatorAt" ON "user_voice"("userId","hideTalkingIndicatorAt");
|
||||
CREATE INDEX "idx_user_voice_userId_talking" ON "user_voice"("meetingId", "userId","talking");
|
||||
CREATE INDEX "idx_user_voice_userId_hideTalkingIndicatorAt" ON "user_voice"("meetingId", "userId","hideTalkingIndicatorAt");
|
||||
|
||||
CREATE OR REPLACE VIEW "v_user_voice" AS
|
||||
SELECT
|
||||
u."meetingId",
|
||||
"user_voice" .*,
|
||||
greatest(coalesce(user_voice."startTime", 0), coalesce(user_voice."endTime", 0)) AS "lastSpeakChangedAt",
|
||||
user_talking."userId" IS NOT NULL "showTalkingIndicator"
|
||||
FROM "user" u
|
||||
JOIN "user_voice" ON "user_voice"."userId" = u."userId"
|
||||
LEFT JOIN "user_voice" user_talking ON (user_talking."userId" = u."userId" and user_talking."talking" IS TRUE)
|
||||
OR (user_talking."userId" = u."userId" and user_talking."hideTalkingIndicatorAt" > now())
|
||||
FROM "user_voice"
|
||||
LEFT JOIN "user_voice" user_talking ON (
|
||||
user_talking."meetingId" = user_voice."meetingId" and
|
||||
user_talking."userId" = user_voice."userId" and
|
||||
user_talking."talking" IS TRUE
|
||||
)
|
||||
OR
|
||||
(
|
||||
user_talking."meetingId" = user_voice."meetingId" and
|
||||
user_talking."userId" = user_voice."userId" and
|
||||
user_talking."hideTalkingIndicatorAt" > now()
|
||||
)
|
||||
WHERE "user_voice"."joined" is true;
|
||||
|
||||
|
||||
@ -588,7 +604,7 @@ CREATE OR REPLACE FUNCTION "update_user_voiceUpdatedAt_func"() RETURNS TRIGGER A
|
||||
BEGIN
|
||||
UPDATE "user"
|
||||
SET "voiceUpdatedAt" = current_timestamp
|
||||
WHERE "userId" = NEW."userId";
|
||||
WHERE "meetingId" = NEW."meetingId" AND "userId" = NEW."userId";
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@ -630,51 +646,58 @@ SELECT
|
||||
greatest(coalesce(user_voice."startTime", 0), coalesce(user_voice."endTime", 0)) AS "lastSpeakChangedAt",
|
||||
user_talking."userId" IS NOT NULL "showTalkingIndicator"
|
||||
FROM "user" u
|
||||
LEFT JOIN "user_voice" ON "user_voice"."userId" = u."userId"
|
||||
LEFT JOIN "user_voice" user_talking ON (user_talking."userId" = u."userId" and user_talking."talking" IS TRUE)
|
||||
OR (user_talking."userId" = u."userId" and user_talking."hideTalkingIndicatorAt" > now());
|
||||
LEFT JOIN "user_voice" ON "user_voice"."meetingId" = u."meetingId" AND "user_voice"."userId" = u."userId"
|
||||
LEFT JOIN "user_voice" user_talking ON (
|
||||
user_talking."meetingId" = u."meetingId" and
|
||||
user_talking."userId" = u."userId" and
|
||||
user_talking."talking" IS TRUE
|
||||
)
|
||||
OR (
|
||||
user_talking."meetingId" = u."meetingId" and
|
||||
user_talking."userId" = u."userId" and
|
||||
user_talking."hideTalkingIndicatorAt" > now()
|
||||
);
|
||||
---TEMPORARY MINIMONGO ADAPTER END
|
||||
|
||||
|
||||
|
||||
CREATE TABLE "user_camera" (
|
||||
"streamId" varchar(100) PRIMARY KEY,
|
||||
"userId" varchar(50) NOT NULL REFERENCES "user"("userId") ON DELETE CASCADE
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX "idx_user_camera_userId" ON "user_camera"("userId");
|
||||
CREATE INDEX "idx_user_camera_userId" ON "user_camera"("meetingId", "userId");
|
||||
|
||||
CREATE OR REPLACE VIEW "v_user_camera" AS
|
||||
SELECT
|
||||
u."meetingId",
|
||||
"user_camera" .*
|
||||
FROM "user_camera"
|
||||
JOIN "user" u ON u."userId" = user_camera."userId";
|
||||
SELECT * FROM "user_camera";
|
||||
|
||||
CREATE TABLE "user_breakoutRoom" (
|
||||
"userId" varchar(50) PRIMARY KEY REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"breakoutRoomId" varchar(100),
|
||||
"isDefaultName" boolean,
|
||||
"sequence" int,
|
||||
"shortName" varchar(100),
|
||||
"currentlyInRoom" boolean
|
||||
"currentlyInRoom" boolean,
|
||||
CONSTRAINT "user_breakoutRoom_pkey" PRIMARY KEY ("meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
--CREATE INDEX "idx_user_breakoutRoom_userId" ON "user_breakoutRoom"("userId");
|
||||
--CREATE INDEX "idx_user_breakoutRoom_userId" ON "user_breakoutRoom"("meetingId", "userId");
|
||||
|
||||
CREATE OR REPLACE VIEW "v_user_breakoutRoom" AS
|
||||
SELECT
|
||||
u."meetingId",
|
||||
"user_breakoutRoom" .*
|
||||
FROM "user_breakoutRoom"
|
||||
JOIN "user" u ON u."userId" = "user_breakoutRoom"."userId";
|
||||
SELECT * FROM "user_breakoutRoom";
|
||||
|
||||
CREATE TABLE "user_connectionStatus" (
|
||||
"userId" varchar(50) PRIMARY KEY REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100) REFERENCES "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"connectionAliveAtMaxIntervalMs" numeric,
|
||||
"connectionAliveAt" timestamp with time zone,
|
||||
"networkRttInMs" numeric,
|
||||
"status" varchar(25),
|
||||
"statusUpdatedAt" timestamp with time zone
|
||||
"statusUpdatedAt" timestamp with time zone,
|
||||
CONSTRAINT "user_connectionStatus_voice_pkey" PRIMARY KEY ("meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
create index "idx_user_connectionStatus_meetingId" on "user_connectionStatus"("meetingId");
|
||||
|
||||
@ -718,7 +741,8 @@ EXECUTE FUNCTION "update_connectionAliveAtMaxIntervalMs"();
|
||||
--);
|
||||
|
||||
CREATE TABLE "user_connectionStatusMetrics" (
|
||||
"userId" varchar(50) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"status" varchar(25),
|
||||
"occurrencesCount" integer,
|
||||
"firstOccurrenceAt" timestamp with time zone,
|
||||
@ -726,10 +750,11 @@ CREATE TABLE "user_connectionStatusMetrics" (
|
||||
"lowestNetworkRttInMs" numeric,
|
||||
"highestNetworkRttInMs" numeric,
|
||||
"lastNetworkRttInMs" numeric,
|
||||
CONSTRAINT "user_connectionStatusMetrics_pkey" PRIMARY KEY ("userId","status")
|
||||
CONSTRAINT "user_connectionStatusMetrics_pkey" PRIMARY KEY ("meetingId","userId","status"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
create index "idx_user_connectionStatusMetrics_userId" on "user_connectionStatusMetrics"("userId");
|
||||
create index "idx_user_connectionStatusMetrics_userId" on "user_connectionStatusMetrics"("meetingId","userId");
|
||||
|
||||
--This function populate rtt, status and the table user_connectionStatusMetrics
|
||||
CREATE OR REPLACE FUNCTION "update_user_connectionStatus_trigger_func"() RETURNS TRIGGER AS $$
|
||||
@ -745,10 +770,10 @@ BEGIN
|
||||
"lowestNetworkRttInMs" = LEAST("user_connectionStatusMetrics"."lowestNetworkRttInMs",NEW."networkRttInMs"),
|
||||
"lastNetworkRttInMs" = NEW."networkRttInMs",
|
||||
"lastOccurrenceAt" = current_timestamp
|
||||
WHERE "userId"=NEW."userId" AND "status"= NEW."status" RETURNING *)
|
||||
INSERT INTO "user_connectionStatusMetrics"("userId","status","occurrencesCount", "firstOccurrenceAt",
|
||||
WHERE "meetingId"=NEW."meetingId" AND "userId"=NEW."userId" AND "status"= NEW."status" RETURNING *)
|
||||
INSERT INTO "user_connectionStatusMetrics"("meetingId","userId","status","occurrencesCount", "firstOccurrenceAt",
|
||||
"highestNetworkRttInMs", "lowestNetworkRttInMs", "lastNetworkRttInMs")
|
||||
SELECT NEW."userId", NEW."status", 1, current_timestamp,
|
||||
SELECT NEW."meetingId", NEW."userId", NEW."status", 1, current_timestamp,
|
||||
NEW."networkRttInMs", NEW."networkRttInMs", NEW."networkRttInMs"
|
||||
WHERE NOT EXISTS (SELECT * FROM upsert);
|
||||
|
||||
@ -768,11 +793,11 @@ CASE WHEN max(cs."connectionAliveAt") < current_timestamp - INTERVAL '1 millisec
|
||||
(array_agg(csm."status" ORDER BY csm."lastOccurrenceAt" DESC))[1] as "lastUnstableStatus",
|
||||
max(csm."lastOccurrenceAt") AS "lastUnstableStatusAt"
|
||||
FROM "user" u
|
||||
JOIN "user_connectionStatus" cs ON cs."userId" = u."userId"
|
||||
LEFT JOIN "user_connectionStatusMetrics" csm ON csm."userId" = u."userId" AND csm."status" != 'normal'
|
||||
JOIN "user_connectionStatus" cs ON cs."meetingId" = u."meetingId" and cs."userId" = u."userId"
|
||||
LEFT JOIN "user_connectionStatusMetrics" csm ON csm."meetingId" = u."meetingId" AND csm."userId" = u."userId" AND csm."status" != 'normal'
|
||||
GROUP BY u."meetingId", u."userId";
|
||||
|
||||
CREATE INDEX "idx_user_connectionStatusMetrics_UnstableReport" ON "user_connectionStatusMetrics" ("userId") WHERE "status" != 'normal';
|
||||
CREATE INDEX "idx_user_connectionStatusMetrics_UnstableReport" ON "user_connectionStatusMetrics" ("meetingId", "userId") WHERE "status" != 'normal';
|
||||
|
||||
|
||||
CREATE TABLE "user_graphqlConnection" (
|
||||
@ -806,23 +831,27 @@ CREATE INDEX "idx_user_graphqlConnectionSessionToken" ON "user_graphqlConnection
|
||||
--LEFT JOIN "user_connectionStatus" uc ON uc."userId" = u."userId";
|
||||
|
||||
CREATE TABLE "user_clientSettings"(
|
||||
"userId" varchar(50) PRIMARY KEY REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100) references "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"userClientSettingsJson" jsonb
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"userClientSettingsJson" jsonb,
|
||||
CONSTRAINT "user_clientSettings_pkey" PRIMARY KEY ("meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX "idx_user_clientSettings_meetingId" ON "user_clientSettings"("meetingId");
|
||||
CREATE INDEX "idx_user_clientSettings_userId" ON "user_clientSettings"("userId");
|
||||
CREATE INDEX "idx_user_clientSettings_userId" ON "user_clientSettings"("meetingId", "userId");
|
||||
|
||||
create view "v_user_clientSettings" as select * from "user_clientSettings";
|
||||
|
||||
|
||||
CREATE TABLE "user_reaction" (
|
||||
"userId" varchar(50) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"reactionEmoji" varchar(25),
|
||||
"durationInSeconds" integer not null,
|
||||
"createdAt" timestamp with time zone not null,
|
||||
"expiresAt" timestamp with time zone
|
||||
"expiresAt" timestamp with time zone,
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
--Set expiresAt on isert or update user_reaction
|
||||
@ -841,29 +870,29 @@ EXECUTE FUNCTION "update_user_reaction_trigger_func"();
|
||||
|
||||
--ALTER TABLE "user_reaction" ADD COLUMN "expiresAt" timestamp with time zone GENERATED ALWAYS AS ("createdAt" + '1 seconds'::INTERVAL * "durationInSeconds") STORED;
|
||||
|
||||
CREATE INDEX "idx_user_reaction_userId_createdAt" ON "user_reaction"("userId", "expiresAt");
|
||||
CREATE INDEX "idx_user_reaction_userId_createdAt" ON "user_reaction"("meetingId", "userId", "expiresAt");
|
||||
|
||||
CREATE VIEW v_user_reaction AS
|
||||
SELECT u."meetingId", ur."userId", ur."reactionEmoji", ur."createdAt", ur."expiresAt"
|
||||
FROM "user" u
|
||||
JOIN "user_reaction" ur ON u."userId" = ur."userId" AND "expiresAt" > current_timestamp;
|
||||
SELECT ur."meetingId", ur."userId", ur."reactionEmoji", ur."createdAt", ur."expiresAt"
|
||||
FROM "user_reaction" ur;
|
||||
|
||||
CREATE VIEW v_user_reaction_current AS
|
||||
SELECT u."meetingId", ur."userId", (array_agg(ur."reactionEmoji" ORDER BY ur."expiresAt" DESC))[1] as "reactionEmoji"
|
||||
FROM "user" u
|
||||
JOIN "user_reaction" ur ON u."userId" = ur."userId" AND "expiresAt" > current_timestamp
|
||||
GROUP BY u."meetingId", ur."userId";
|
||||
SELECT ur."meetingId", ur."userId", (array_agg(ur."reactionEmoji" ORDER BY ur."expiresAt" DESC))[1] as "reactionEmoji"
|
||||
FROM "user_reaction" ur
|
||||
GROUP BY ur."meetingId", ur."userId";
|
||||
|
||||
CREATE TABLE "user_transcriptionError"(
|
||||
"userId" varchar(50) PRIMARY KEY REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100) references "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"errorCode" varchar(255),
|
||||
"errorMessage" text,
|
||||
"lastUpdatedAt" timestamp with time zone DEFAULT now()
|
||||
"lastUpdatedAt" timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT "user_transcriptionError_pkey" PRIMARY KEY ("meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX "idx_user_transcriptionError_meetingId" ON "user_transcriptionError"("meetingId");
|
||||
CREATE INDEX "idx_user_transcriptionError_userId" ON "user_transcriptionError"("userId");
|
||||
CREATE INDEX "idx_user_transcriptionError_userId" ON "user_transcriptionError"("meetingId", "userId");
|
||||
|
||||
create view "v_user_transcriptionError" as select * from "user_transcriptionError";
|
||||
|
||||
@ -873,7 +902,7 @@ create view "v_user_transcriptionError" as select * from "user_transcriptionErro
|
||||
create view "v_meeting" as
|
||||
select "meeting".*, "user_ended"."name" as "endedByUserName"
|
||||
from "meeting"
|
||||
left join "user" "user_ended" on "user_ended"."userId" = "meeting"."endedBy"
|
||||
left join "user" "user_ended" on "user_ended"."meetingId" = "meeting"."meetingId" and "user_ended"."userId" = "meeting"."endedBy"
|
||||
;
|
||||
|
||||
create view "v_meeting_learningDashboard" as
|
||||
@ -1004,8 +1033,12 @@ LEFT JOIN "chat_user" cu ON cu."meetingId" = "user"."meetingId" AND cu."userId"
|
||||
--now it will always add chat_user for public chat onUserJoin
|
||||
--JOIN "chat" ON "user"."meetingId" = chat."meetingId" AND (cu."chatId" = chat."chatId" OR chat."chatId" = 'MAIN-PUBLIC-GROUP-CHAT')
|
||||
JOIN "chat" ON "user"."meetingId" = chat."meetingId" AND cu."chatId" = chat."chatId"
|
||||
LEFT JOIN "chat_user" chat_with ON chat_with."meetingId" = chat."meetingId" AND chat_with."chatId" = chat."chatId" AND chat."chatId" != 'MAIN-PUBLIC-GROUP-CHAT' AND chat_with."userId" != cu."userId"
|
||||
LEFT JOIN chat_message cm ON cm."meetingId" = chat."meetingId" AND cm."chatId" = chat."chatId"
|
||||
LEFT JOIN "chat_user" chat_with ON chat_with."meetingId" = chat."meetingId" AND
|
||||
chat_with."chatId" = chat."chatId" AND
|
||||
chat."chatId" != 'MAIN-PUBLIC-GROUP-CHAT' AND
|
||||
chat_with."userId" != cu."userId"
|
||||
LEFT JOIN chat_message cm ON cm."meetingId" = chat."meetingId" AND
|
||||
cm."chatId" = chat."chatId"
|
||||
WHERE cu."visible" is true
|
||||
GROUP BY "user"."userId", chat."meetingId", chat."chatId", cu."visible", cu."lastSeenAt", chat_with."userId";
|
||||
|
||||
@ -1015,14 +1048,23 @@ FROM chat_message cm
|
||||
WHERE cm."chatId" = 'MAIN-PUBLIC-GROUP-CHAT';
|
||||
|
||||
CREATE OR REPLACE VIEW "v_chat_message_private" AS
|
||||
SELECT cu."userId",
|
||||
cm.*
|
||||
SELECT cu."meetingId",
|
||||
cu."userId",
|
||||
cm."messageId",
|
||||
cm."chatId",
|
||||
cm."correlationId",
|
||||
cm."chatEmphasizedText",
|
||||
cm."message",
|
||||
cm."messageType",
|
||||
cm."messageMetadata",
|
||||
cm."senderId",
|
||||
cm."senderName",
|
||||
cm."senderRole",
|
||||
cm."createdAt"
|
||||
FROM chat_message cm
|
||||
JOIN chat_user cu ON cu."meetingId" = cm."meetingId" AND cu."chatId" = cm."chatId"
|
||||
WHERE cm."chatId" != 'MAIN-PUBLIC-GROUP-CHAT';
|
||||
|
||||
|
||||
|
||||
--============ Presentation / Annotation
|
||||
|
||||
|
||||
@ -1170,6 +1212,7 @@ and pres_presentation."current" IS TRUE;
|
||||
CREATE TABLE "pres_annotation" (
|
||||
"annotationId" varchar(100) PRIMARY KEY,
|
||||
"pageId" varchar(100) REFERENCES "pres_page"("pageId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"annotationInfo" TEXT,
|
||||
"lastHistorySequence" integer,
|
||||
@ -1182,6 +1225,7 @@ CREATE TABLE "pres_annotation_history" (
|
||||
"sequence" serial PRIMARY KEY,
|
||||
"annotationId" varchar(100),
|
||||
"pageId" varchar(100) REFERENCES "pres_page"("pageId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"annotationInfo" TEXT
|
||||
-- "lastUpdatedAt" timestamp with time zone DEFAULT now()
|
||||
@ -1189,7 +1233,7 @@ CREATE TABLE "pres_annotation_history" (
|
||||
CREATE INDEX "idx_pres_annotation_history_pageId" ON "pres_annotation"("pageId");
|
||||
|
||||
CREATE VIEW "v_pres_annotation_curr" AS
|
||||
SELECT p."meetingId", pp."presentationId", pa.*
|
||||
SELECT p."meetingId", pp."presentationId", pa."annotationId", pa."pageId", pa."userId", pa."annotationInfo", pa."lastHistorySequence", pa."lastUpdatedAt"
|
||||
FROM pres_presentation p
|
||||
JOIN pres_page pp ON pp."presentationId" = p."presentationId"
|
||||
JOIN pres_annotation pa ON pa."pageId" = pp."pageId"
|
||||
@ -1197,7 +1241,7 @@ WHERE p."current" IS true
|
||||
AND pp."current" IS true;
|
||||
|
||||
CREATE VIEW "v_pres_annotation_history_curr" AS
|
||||
SELECT p."meetingId", pp."presentationId", pah.*
|
||||
SELECT p."meetingId", pp."presentationId", pah."pageId", pah."userId", pah."annotationId", pah."annotationInfo", pah."sequence"
|
||||
FROM pres_presentation p
|
||||
JOIN pres_page pp ON pp."presentationId" = p."presentationId"
|
||||
JOIN pres_annotation_history pah ON pah."pageId" = pp."pageId"
|
||||
@ -1206,20 +1250,20 @@ AND pp."current" IS true;
|
||||
|
||||
CREATE TABLE "pres_page_writers" (
|
||||
"pageId" varchar(100) REFERENCES "pres_page"("pageId") ON DELETE CASCADE,
|
||||
"userId" varchar(50) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"changedModeOn" bigint,
|
||||
CONSTRAINT "pres_page_writers_pkey" PRIMARY KEY ("pageId","userId")
|
||||
CONSTRAINT "pres_page_writers_pkey" PRIMARY KEY ("pageId","meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
create index "idx_pres_page_writers_userID" on "pres_page_writers"("userId");
|
||||
create index "idx_pres_page_writers_userID" on "pres_page_writers"("meetingId", "userId");
|
||||
|
||||
CREATE OR REPLACE VIEW "v_pres_page_writers" AS
|
||||
SELECT
|
||||
u."meetingId",
|
||||
"pres_presentation"."presentationId",
|
||||
"pres_page_writers" .*,
|
||||
CASE WHEN pres_presentation."current" IS true AND pres_page."current" IS true THEN true ELSE false END AS "isCurrentPage"
|
||||
FROM "pres_page_writers"
|
||||
JOIN "user" u ON u."userId" = "pres_page_writers"."userId"
|
||||
JOIN "pres_page" ON "pres_page"."pageId" = "pres_page_writers"."pageId"
|
||||
JOIN "pres_presentation" ON "pres_presentation"."presentationId" = "pres_page"."presentationId" ;
|
||||
|
||||
@ -1251,7 +1295,8 @@ BEGIN
|
||||
CASE WHEN presenter THEN TRUE
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM "v_pres_page_writers" v
|
||||
WHERE v."userId" = "user"."userId"
|
||||
WHERE v."meetingId" = "user"."meetingId"
|
||||
AND v."userId" = "user"."userId"
|
||||
AND v."isCurrentPage" IS TRUE
|
||||
) THEN TRUE
|
||||
ELSE FALSE
|
||||
@ -1266,7 +1311,7 @@ $$ LANGUAGE plpgsql;
|
||||
CREATE OR REPLACE FUNCTION update_user_presenter_trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF OLD."presenter" <> NEW."presenter" THEN
|
||||
PERFORM "update_user_hasDrawPermissionOnCurrentPage"(NEW."userId", NULL);
|
||||
PERFORM "update_user_hasDrawPermissionOnCurrentPage"(NEW."userId", NEW."meetingId");
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
@ -1309,9 +1354,9 @@ CREATE OR REPLACE FUNCTION ins_upd_del_pres_page_writers_trigger_func()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'UPDATE' or TG_OP = 'INSERT' THEN
|
||||
PERFORM "update_user_hasDrawPermissionOnCurrentPage"(NEW."userId", NULL);
|
||||
PERFORM "update_user_hasDrawPermissionOnCurrentPage"(NEW."userId", NEW."meetingId");
|
||||
ELSIF TG_OP = 'DELETE' THEN
|
||||
PERFORM "update_user_hasDrawPermissionOnCurrentPage"(OLD."userId", NULL);
|
||||
PERFORM "update_user_hasDrawPermissionOnCurrentPage"(OLD."userId", NEW."meetingId");
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
@ -1326,18 +1371,20 @@ FOR EACH ROW EXECUTE FUNCTION ins_upd_del_pres_page_writers_trigger_func();
|
||||
|
||||
CREATE TABLE "pres_page_cursor" (
|
||||
"pageId" varchar(100) REFERENCES "pres_page"("pageId") ON DELETE CASCADE,
|
||||
"userId" varchar(50) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"xPercent" numeric,
|
||||
"yPercent" numeric,
|
||||
"lastUpdatedAt" timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT "pres_page_cursor_pkey" PRIMARY KEY ("pageId","userId")
|
||||
CONSTRAINT "pres_page_cursor_pkey" PRIMARY KEY ("pageId","meetingId","userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
create index "idx_pres_page_cursor_pageId" on "pres_page_cursor"("pageId");
|
||||
create index "idx_pres_page_cursor_userID" on "pres_page_cursor"("userId");
|
||||
create index "idx_pres_page_cursor_userID" on "pres_page_cursor"("meetingId","userId");
|
||||
create index "idx_pres_page_cursor_lastUpdatedAt" on "pres_page_cursor"("pageId","lastUpdatedAt");
|
||||
|
||||
CREATE VIEW "v_pres_page_cursor" AS
|
||||
SELECT pres_presentation."meetingId", pres_page."presentationId", c.*,
|
||||
SELECT pres_page."presentationId", c.*,
|
||||
CASE WHEN pres_presentation."current" IS true AND pres_page."current" IS true THEN true ELSE false END AS "isCurrentPage"
|
||||
FROM pres_page_cursor c
|
||||
JOIN pres_page ON pres_page."pageId" = c."pageId"
|
||||
@ -1349,8 +1396,8 @@ JOIN pres_presentation ON pres_presentation."presentationId" = pres_page."presen
|
||||
|
||||
CREATE TABLE "poll" (
|
||||
"pollId" varchar(100) PRIMARY KEY,
|
||||
"meetingId" varchar(100) REFERENCES "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"ownerId" varchar(100) REFERENCES "user"("userId"),
|
||||
"meetingId" varchar(100),
|
||||
"ownerId" varchar(100),
|
||||
"questionText" TEXT,
|
||||
"type" varchar(30),
|
||||
"secret" boolean,
|
||||
@ -1358,9 +1405,11 @@ CREATE TABLE "poll" (
|
||||
"ended" boolean,
|
||||
"published" boolean,
|
||||
"publishedAt" timestamp with time zone,
|
||||
"createdAt" timestamp with time zone not null default current_timestamp
|
||||
"createdAt" timestamp with time zone not null default current_timestamp,
|
||||
FOREIGN KEY ("meetingId", "ownerId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX "idx_poll_meetingId" ON "poll"("meetingId");
|
||||
CREATE INDEX "idx_poll_ownerId" ON "poll"("meetingId","ownerId");
|
||||
CREATE INDEX "idx_poll_meetingId_active" ON "poll"("meetingId") where ended is false;
|
||||
CREATE INDEX "idx_poll_meetingId_published" ON "poll"("meetingId") where published is true;
|
||||
|
||||
@ -1375,12 +1424,14 @@ CREATE INDEX "idx_poll_option_pollId" ON "poll_option"("pollId");
|
||||
CREATE TABLE "poll_response" (
|
||||
"pollId" varchar(100),
|
||||
"optionId" integer,
|
||||
"userId" varchar(100) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
FOREIGN KEY ("pollId", "optionId") REFERENCES "poll_option"("pollId", "optionId") ON DELETE CASCADE
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(100),
|
||||
FOREIGN KEY ("pollId", "optionId") REFERENCES "poll_option"("pollId", "optionId") ON DELETE CASCADE,
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX "idx_poll_response_pollId" ON "poll_response"("pollId");
|
||||
CREATE INDEX "idx_poll_response_userId" ON "poll_response"("userId");
|
||||
CREATE INDEX "idx_poll_response_pollId_userId" ON "poll_response"("pollId", "userId");
|
||||
CREATE INDEX "idx_poll_response_userId" ON "poll_response"("meetingId", "userId");
|
||||
CREATE INDEX "idx_poll_response_pollId_userId" ON "poll_response"("pollId", "meetingId", "userId");
|
||||
|
||||
CREATE OR REPLACE VIEW "v_poll_response" AS
|
||||
SELECT
|
||||
@ -1388,6 +1439,7 @@ poll."meetingId",
|
||||
poll."pollId",
|
||||
poll."type",
|
||||
poll."questionText",
|
||||
poll."meetingId" AS "pollOwnerMeetingId",
|
||||
poll."ownerId" AS "pollOwnerId",
|
||||
poll.published,
|
||||
o."optionId",
|
||||
@ -1402,12 +1454,13 @@ ORDER BY poll."pollId";
|
||||
|
||||
CREATE VIEW "v_poll_user" AS
|
||||
SELECT
|
||||
poll."meetingId",
|
||||
poll."meetingId" AS "pollOwnerMeetingId",
|
||||
poll."ownerId" AS "pollOwnerId",
|
||||
u."meetingId",
|
||||
u."userId",
|
||||
poll."pollId",
|
||||
poll."type",
|
||||
poll."questionText",
|
||||
poll."ownerId" AS "pollOwnerId",
|
||||
u."userId",
|
||||
array_remove(array_agg(o."optionId"), NULL) AS "optionIds",
|
||||
array_remove(array_agg(o."optionDesc"), NULL) AS "optionDescIds",
|
||||
CASE WHEN count(o."optionId") > 0 THEN TRUE ELSE FALSE end responded
|
||||
@ -1415,7 +1468,7 @@ FROM poll
|
||||
JOIN v_user u ON u."meetingId" = poll."meetingId" AND "isDialIn" IS FALSE AND presenter IS FALSE
|
||||
LEFT JOIN poll_response r ON r."pollId" = poll."pollId" AND r."userId" = u."userId"
|
||||
LEFT JOIN poll_option o ON o."pollId" = r."pollId" AND o."optionId" = r."optionId"
|
||||
GROUP BY poll."pollId", u."userId", u.name ;
|
||||
GROUP BY poll."pollId", u."meetingId", u."userId";
|
||||
|
||||
CREATE VIEW "v_poll" AS SELECT * FROM "poll";
|
||||
|
||||
@ -1426,11 +1479,13 @@ JOIN poll using("pollId")
|
||||
WHERE poll."type" != 'R-';
|
||||
|
||||
create view "v_poll_user_current" as
|
||||
select "user"."userId", "poll"."pollId", case when count(pr.*) > 0 then true else false end as responded
|
||||
select "user"."meetingId", "user"."userId", "poll"."pollId", case when count(pr.*) > 0 then true else false end as responded
|
||||
from "user"
|
||||
join "poll" on "poll"."meetingId" = "user"."meetingId"
|
||||
left join "poll_response" pr on pr."userId" = "user"."userId" and pr."pollId" = "poll"."pollId"
|
||||
group by "user"."userId", "poll"."pollId";
|
||||
left join "poll_response" pr on pr."meetingId" = "user"."meetingId" and
|
||||
pr."userId" = "user"."userId" and
|
||||
pr."pollId" = "poll"."pollId"
|
||||
group by "user"."meetingId", "user"."userId", "poll"."pollId";
|
||||
|
||||
--------------------------------
|
||||
----External video
|
||||
@ -1538,12 +1593,14 @@ CREATE INDEX "idx_breakoutRoom_parentMeetingId" ON "breakoutRoom"("parentMeeting
|
||||
|
||||
CREATE TABLE "breakoutRoom_user" (
|
||||
"breakoutRoomId" varchar(100) NOT NULL REFERENCES "breakoutRoom"("breakoutRoomId") ON DELETE CASCADE,
|
||||
"userId" varchar(50) NOT NULL REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"meetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"joinURL" text,
|
||||
"assignedAt" timestamp with time zone,
|
||||
"joinedAt" timestamp with time zone,
|
||||
"inviteDismissedAt" timestamp with time zone,
|
||||
CONSTRAINT "breakoutRoom_user_pkey" PRIMARY KEY ("breakoutRoomId", "userId")
|
||||
CONSTRAINT "breakoutRoom_user_pkey" PRIMARY KEY ("breakoutRoomId", "meetingId", "userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW "v_breakoutRoom" AS
|
||||
@ -1564,20 +1621,20 @@ SELECT *,
|
||||
AND ("isModerator" is false OR "sendInvitationToModerators")
|
||||
THEN TRUE ELSE FALSE END "showInvitation"
|
||||
from (
|
||||
SELECT u."userId", b."parentMeetingId", b."breakoutRoomId", b."freeJoin", b."sequence", b."name", b."isDefaultName",
|
||||
SELECT u."meetingId" as "userMeetingId", u."userId", b."parentMeetingId", b."breakoutRoomId", b."freeJoin", b."sequence", b."name", b."isDefaultName",
|
||||
b."shortName", b."startedAt", b."endedAt", b."durationInSeconds", b."sendInvitationToModerators",
|
||||
bu."assignedAt", bu."joinURL", bu."inviteDismissedAt", u."role" = 'MODERATOR' as "isModerator",
|
||||
--CASE WHEN b."durationInSeconds" = 0 THEN NULL ELSE b."startedAt" + b."durationInSeconds" * '1 second'::INTERVAL END AS "willEndAt",
|
||||
ub."isOnline" AS "currentRoomIsOnline",
|
||||
ub."registeredAt" AS "currentRoomRegisteredAt",
|
||||
ub."joined" AS "currentRoomJoined",
|
||||
rank() OVER (partition BY u."userId" order by "assignedAt" desc nulls last) as "currentRoomPriority",
|
||||
max(bu."joinedAt") OVER (partition BY u."userId") AS "lastRoomJoinedAt",
|
||||
max(bu."breakoutRoomId") OVER (partition BY u."userId" ORDER BY bu."joinedAt") AS "lastRoomJoinedId",
|
||||
sum(CASE WHEN ub."isOnline" THEN 1 ELSE 0 END) OVER (partition BY u."userId") > 0 as "lastRoomIsOnline"
|
||||
rank() OVER (partition BY u."meetingId", u."userId" order by "assignedAt" desc nulls last) as "currentRoomPriority",
|
||||
max(bu."joinedAt") OVER (partition BY u."meetingId", u."userId") AS "lastRoomJoinedAt",
|
||||
max(bu."breakoutRoomId") OVER (partition BY u."meetingId", u."userId" ORDER BY bu."joinedAt") AS "lastRoomJoinedId",
|
||||
sum(CASE WHEN ub."isOnline" THEN 1 ELSE 0 END) OVER (partition BY u."meetingId", u."userId") > 0 as "lastRoomIsOnline"
|
||||
FROM "user" u
|
||||
JOIN "breakoutRoom" b ON b."parentMeetingId" = u."meetingId"
|
||||
LEFT JOIN "breakoutRoom_user" bu ON bu."userId" = u."userId" AND bu."breakoutRoomId" = b."breakoutRoomId"
|
||||
LEFT JOIN "breakoutRoom_user" bu ON bu."meetingId" = u."meetingId" AND bu."userId" = u."userId" AND bu."breakoutRoomId" = b."breakoutRoomId"
|
||||
LEFT JOIN "meeting" mb ON mb."extId" = b."externalId"
|
||||
LEFT JOIN "v_user" ub ON ub."meetingId" = mb."meetingId" and ub."extId" = u."extId" || '-' || b."sequence"
|
||||
WHERE (bu."assignedAt" IS NOT NULL
|
||||
@ -1587,16 +1644,32 @@ from (
|
||||
) a;
|
||||
|
||||
CREATE OR REPLACE VIEW "v_breakoutRoom_assignedUser" AS
|
||||
SELECT "parentMeetingId", "breakoutRoomId", "userId"
|
||||
SELECT "parentMeetingId", "breakoutRoomId", "userMeetingId", "userId"
|
||||
FROM "v_breakoutRoom"
|
||||
WHERE "assignedAt" IS NOT NULL;
|
||||
|
||||
--TODO improve performance (and handle two users with same extId)
|
||||
CREATE OR REPLACE VIEW "v_breakoutRoom_participant" AS
|
||||
SELECT DISTINCT "parentMeetingId", "breakoutRoomId", "userId"
|
||||
CREATE OR REPLACE VIEW "v_breakoutRoom_participant" as
|
||||
SELECT DISTINCT
|
||||
"parentMeetingId",
|
||||
"breakoutRoomId",
|
||||
"userMeetingId",
|
||||
"userId",
|
||||
false as "isAudioOnly"
|
||||
FROM "v_breakoutRoom"
|
||||
WHERE "currentRoomIsOnline" IS TRUE;
|
||||
--SELECT DISTINCT br."parentMeetingId", br."breakoutRoomId", "user"."userId"
|
||||
WHERE "currentRoomIsOnline" IS TRUE
|
||||
union --include users that joined only with audio
|
||||
select parent_user."meetingId" as "parentMeetingId",
|
||||
bk_user."meetingId" as "breakoutRoomId",
|
||||
parent_user."meetingId" as "userMeetingId",
|
||||
parent_user."userId",
|
||||
true as "isAudioOnly"
|
||||
from "user" bk_user
|
||||
join "user" parent_user on parent_user."userId" = bk_user."userId" and parent_user."transferredFromParentMeeting" is false
|
||||
where bk_user."transferredFromParentMeeting" is true
|
||||
and bk_user."loggedOut" is false;
|
||||
|
||||
--SELECT DISTINCT br."parentMeetingId", br."breakoutRoomId", "user"."meetingId", "user"."userId"
|
||||
--FROM v_user "user"
|
||||
--JOIN "meeting" m using("meetingId")
|
||||
--JOIN "v_meeting_breakoutPolicies" vmbp using("meetingId")
|
||||
@ -1610,7 +1683,8 @@ where bu."breakoutRoomId" in (
|
||||
select b."breakoutRoomId"
|
||||
from "user" u
|
||||
join "breakoutRoom" b on b."parentMeetingId" = u."meetingId" and b."endedAt" is null
|
||||
where u."userId" = bu."userId"
|
||||
where u."meetingId" = bu."meetingId"
|
||||
and u."userId" = bu."userId"
|
||||
);
|
||||
|
||||
------------------------------------
|
||||
@ -1630,13 +1704,14 @@ create table "sharedNotes_rev" (
|
||||
"meetingId" varchar(100) references "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"sharedNotesExtId" varchar(25),
|
||||
"rev" integer,
|
||||
"userId" varchar(50) references "user"("userId") ON DELETE SET NULL,
|
||||
"userId" varchar(50),
|
||||
"changeset" text,
|
||||
"start" integer,
|
||||
"end" integer,
|
||||
"diff" TEXT,
|
||||
"createdAt" timestamp with time zone,
|
||||
constraint "pk_sharedNotes_rev" primary key ("meetingId", "sharedNotesExtId", "rev")
|
||||
constraint "pk_sharedNotes_rev" primary key ("meetingId", "sharedNotesExtId", "rev"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE SET NULL
|
||||
);
|
||||
--create view "v_sharedNotes_rev" as select * from "sharedNotes_rev";
|
||||
|
||||
@ -1648,11 +1723,12 @@ where "diff" is not null;
|
||||
create table "sharedNotes_session" (
|
||||
"meetingId" varchar(100) references "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"sharedNotesExtId" varchar(25),
|
||||
"userId" varchar(50) references "user"("userId") ON DELETE CASCADE,
|
||||
"userId" varchar(50),
|
||||
"sessionId" varchar(50),
|
||||
constraint "pk_sharedNotes_session" primary key ("meetingId", "sharedNotesExtId", "userId")
|
||||
constraint "pk_sharedNotes_session" primary key ("meetingId", "sharedNotesExtId", "userId"),
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
create index "sharedNotes_session_userId" on "sharedNotes_session"("userId");
|
||||
create index "sharedNotes_session_userId" on "sharedNotes_session"("meetingId", "userId");
|
||||
|
||||
create view "v_sharedNotes" as
|
||||
SELECT sn.*, max(snr.rev) "lastRev"
|
||||
@ -1673,32 +1749,62 @@ SELECT
|
||||
FLOOR(EXTRACT(EPOCH FROM current_timestamp) * 1000)::bigint AS "currentTimeMillis";
|
||||
|
||||
------------------------------------
|
||||
----audioCaption
|
||||
----audioCaption or typedCaption
|
||||
|
||||
CREATE TABLE "caption_locale" (
|
||||
"meetingId" varchar(100) NOT NULL REFERENCES "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"locale" varchar(15) NOT NULL,
|
||||
"captionType" varchar(100) NOT NULL, --Audio Transcription or Typed Caption
|
||||
"ownerUserId" varchar(50),
|
||||
"createdAt" timestamp with time zone default current_timestamp,
|
||||
"updatedAt" timestamp with time zone,
|
||||
CONSTRAINT "caption_locale_pk" primary key ("meetingId","locale","captionType"),
|
||||
FOREIGN KEY ("meetingId", "ownerUserId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE "caption" (
|
||||
"captionId" varchar(100) NOT NULL PRIMARY KEY,
|
||||
"meetingId" varchar(100) NOT NULL REFERENCES "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"captionType" varchar(100) NOT NULL, --Audio Transcription or Typed Caption
|
||||
"userId" varchar(50) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"lang" varchar(15),
|
||||
"userId" varchar(50),
|
||||
"locale" varchar(15),
|
||||
"captionText" text,
|
||||
"createdAt" timestamp with time zone
|
||||
"createdAt" timestamp with time zone,
|
||||
FOREIGN KEY ("meetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
create index idx_caption on caption("meetingId","lang","createdAt");
|
||||
create index idx_caption_captionType on caption("meetingId","lang","captionType","createdAt");
|
||||
CREATE OR REPLACE FUNCTION "update_caption_locale_owner_func"() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
WITH upsert AS (
|
||||
UPDATE "caption_locale" SET
|
||||
"ownerUserId" = NEW."userId",
|
||||
"updatedAt" = current_timestamp
|
||||
WHERE "meetingId"=NEW."meetingId" AND "locale"=NEW."locale" AND "captionType"= NEW."captionType"
|
||||
RETURNING *)
|
||||
INSERT INTO "caption_locale"("meetingId","locale","captionType","ownerUserId")
|
||||
SELECT NEW."meetingId", NEW."locale", NEW."captionType", NEW."userId"
|
||||
WHERE NOT EXISTS (SELECT * FROM upsert);
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER "insert_caption_trigger" BEFORE INSERT ON "caption" FOR EACH ROW
|
||||
EXECUTE FUNCTION "update_caption_locale_owner_func"();
|
||||
|
||||
create index idx_caption on caption("meetingId","locale","createdAt");
|
||||
create index idx_caption_captionType on caption("meetingId","locale","captionType","createdAt");
|
||||
|
||||
CREATE OR REPLACE VIEW "v_caption" AS
|
||||
SELECT *
|
||||
FROM "caption"
|
||||
WHERE "createdAt" > current_timestamp - INTERVAL '5 seconds';
|
||||
|
||||
CREATE OR REPLACE VIEW "v_caption_typed_activeLocales" AS
|
||||
select distinct "meetingId", "lang", "userId"
|
||||
from "caption"
|
||||
where "captionType" = 'TYPED';
|
||||
CREATE OR REPLACE VIEW "v_caption_activeLocales" AS
|
||||
select distinct "meetingId", "locale", "ownerUserId", "captionType"
|
||||
from "caption_locale";
|
||||
|
||||
create index "idx_caption_typed_activeLocales" on caption("meetingId","lang","userId") where "captionType" = 'TYPED';
|
||||
create index "idx_caption_typed_activeLocales" on caption("meetingId","locale","userId") where "captionType" = 'TYPED';
|
||||
|
||||
------------------------------------
|
||||
----
|
||||
@ -1730,8 +1836,10 @@ CREATE TABLE "notification" (
|
||||
"messageDescription" varchar(100),
|
||||
"messageValues" jsonb,
|
||||
"role" varchar(100), --MODERATOR, PRESENTER, VIEWER
|
||||
"userId" varchar(50) references "user"("userId") ON DELETE CASCADE,
|
||||
"createdAt" timestamp with time zone DEFAULT current_timestamp
|
||||
"userMeetingId" varchar(100),
|
||||
"userId" varchar(50),
|
||||
"createdAt" timestamp with time zone DEFAULT current_timestamp,
|
||||
FOREIGN KEY ("userMeetingId", "userId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
create or replace VIEW "v_notification" AS
|
||||
@ -1763,27 +1871,29 @@ create index idx_notification on notification("meetingId","userId","role","creat
|
||||
---Plugins Data Channel
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE "pluginDataChannelMessage" (
|
||||
CREATE TABLE "pluginDataChannelEntry" (
|
||||
"meetingId" varchar(100) references "meeting"("meetingId") ON DELETE CASCADE,
|
||||
"pluginName" varchar(255),
|
||||
"dataChannel" varchar(255),
|
||||
"messageId" varchar(50) DEFAULT uuid_generate_v4(),
|
||||
"channelName" varchar(255),
|
||||
"entryId" varchar(50) DEFAULT uuid_generate_v4(),
|
||||
"subChannelName" varchar(255),
|
||||
"payloadJson" jsonb,
|
||||
"fromUserId" varchar(50) REFERENCES "user"("userId") ON DELETE CASCADE,
|
||||
"fromUserId" varchar(50),
|
||||
"toRoles" varchar[], --MODERATOR, VIEWER, PRESENTER
|
||||
"toUserIds" varchar[],
|
||||
"createdAt" timestamp with time zone DEFAULT current_timestamp,
|
||||
"deletedAt" timestamp with time zone,
|
||||
CONSTRAINT "pluginDataChannel_pkey" PRIMARY KEY ("meetingId","pluginName","dataChannel","messageId")
|
||||
CONSTRAINT "pluginDataChannel_pkey" PRIMARY KEY ("meetingId","pluginName","channelName","entryId", "subChannelName"),
|
||||
FOREIGN KEY ("meetingId", "fromUserId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
create index "idx_pluginDataChannelMessage_dataChannel" on "pluginDataChannelMessage"("meetingId", "pluginName", "dataChannel", "toRoles", "toUserIds", "createdAt") where "deletedAt" is null;
|
||||
create index "idx_pluginDataChannelMessage_roles" on "pluginDataChannelMessage"("meetingId", "toRoles", "toUserIds", "createdAt") where "deletedAt" is null;
|
||||
create index "idx_pluginDataChannelEntry_channelName" on "pluginDataChannelEntry"("meetingId", "pluginName", "channelName", "toRoles", "toUserIds", "subChannelName", "createdAt") where "deletedAt" is null;
|
||||
create index "idx_pluginDataChannelEntry_roles" on "pluginDataChannelEntry"("meetingId", "toRoles", "toUserIds", "createdAt") where "deletedAt" is null;
|
||||
|
||||
CREATE OR REPLACE VIEW "v_pluginDataChannelMessage" AS
|
||||
SELECT u."meetingId", u."userId", m."pluginName", m."dataChannel", m."messageId", m."payloadJson", m."fromUserId", m."toRoles", m."createdAt"
|
||||
CREATE OR REPLACE VIEW "v_pluginDataChannelEntry" AS
|
||||
SELECT u."meetingId", u."userId", m."pluginName", m."channelName", m."subChannelName", m."entryId", m."payloadJson", m."fromUserId", m."toRoles", m."createdAt"
|
||||
FROM "user" u
|
||||
JOIN "pluginDataChannelMessage" m ON m."meetingId" = u."meetingId"
|
||||
JOIN "pluginDataChannelEntry" m ON m."meetingId" = u."meetingId"
|
||||
AND ((m."toRoles" IS NULL AND m."toUserIds" IS NULL)
|
||||
OR u."userId" = ANY(m."toUserIds")
|
||||
OR u."role" = ANY(m."toRoles")
|
||||
|
@ -201,17 +201,19 @@ type Mutation {
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
pluginDataChannelDeleteMessage(
|
||||
pluginDataChannelDeleteEntry(
|
||||
pluginName: String!
|
||||
dataChannel: String!
|
||||
messageId: String!
|
||||
channelName: String!
|
||||
subChannelName: String!
|
||||
entryId: String!
|
||||
): Boolean
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
pluginDataChannelDispatchMessage(
|
||||
pluginDataChannelPushEntry(
|
||||
pluginName: String!
|
||||
dataChannel: String!
|
||||
subChannelName: String!
|
||||
channelName: String!
|
||||
payloadJson: String!
|
||||
toRoles: [String]!
|
||||
toUserIds: [String]!
|
||||
@ -221,7 +223,8 @@ type Mutation {
|
||||
type Mutation {
|
||||
pluginDataChannelReset(
|
||||
pluginName: String!
|
||||
dataChannel: String!
|
||||
channelName: String!
|
||||
subChannelName: String!
|
||||
): Boolean
|
||||
}
|
||||
|
||||
@ -559,3 +562,11 @@ input GuestUserApprovalStatus {
|
||||
status: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
captionSetOwner(
|
||||
locale: String!
|
||||
ownerUserId: String!
|
||||
): Boolean
|
||||
}
|
||||
|
||||
|
||||
|
@ -179,13 +179,13 @@ actions:
|
||||
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||
permissions:
|
||||
- role: bbb_client
|
||||
- name: pluginDataChannelDeleteMessage
|
||||
- name: pluginDataChannelDeleteEntry
|
||||
definition:
|
||||
kind: synchronous
|
||||
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||
permissions:
|
||||
- role: bbb_client
|
||||
- name: pluginDataChannelDispatchMessage
|
||||
- name: pluginDataChannelPushEntry
|
||||
definition:
|
||||
kind: synchronous
|
||||
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||
@ -493,6 +493,12 @@ actions:
|
||||
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||
permissions:
|
||||
- role: bbb_client
|
||||
- name: captionSetOwner
|
||||
definition:
|
||||
kind: synchronous
|
||||
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||
permissions:
|
||||
- role: bbb_client
|
||||
custom_types:
|
||||
enums: []
|
||||
input_objects:
|
||||
|
@ -51,6 +51,9 @@ select_permissions:
|
||||
- showInvitation
|
||||
- startedAt
|
||||
filter:
|
||||
userId:
|
||||
_eq: X-Hasura-UserId
|
||||
_and:
|
||||
- userMeetingId:
|
||||
_eq: X-Hasura-MeetingId
|
||||
- userId:
|
||||
_eq: X-Hasura-UserId
|
||||
allow_aggregations: true
|
||||
|
@ -12,6 +12,7 @@ object_relationships:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
userId: userId
|
||||
userMeetingId: meetingId
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
name: v_user_ref
|
||||
@ -25,8 +26,11 @@ select_permissions:
|
||||
_or:
|
||||
- parentMeetingId:
|
||||
_eq: X-Hasura-ModeratorInMeeting
|
||||
- userId:
|
||||
_eq: X-Hasura-UserId
|
||||
- _and:
|
||||
- userMeetingId:
|
||||
_eq: X-Hasura-MeetingId
|
||||
- userId:
|
||||
_eq: X-Hasura-UserId
|
||||
allow_aggregations: true
|
||||
query_root_fields: []
|
||||
subscription_root_fields: []
|
||||
|
@ -12,6 +12,7 @@ object_relationships:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
userId: userId
|
||||
userMeetingId: meetingId
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
name: v_user_ref
|
||||
@ -20,6 +21,7 @@ select_permissions:
|
||||
- role: bbb_client
|
||||
permission:
|
||||
columns:
|
||||
- isAudioOnly
|
||||
- userId
|
||||
filter:
|
||||
parentMeetingId:
|
||||
|
@ -16,8 +16,11 @@ select_permissions:
|
||||
- joinURL
|
||||
- joinedAt
|
||||
filter:
|
||||
userId:
|
||||
_eq: X-Hasura-UserId
|
||||
_and:
|
||||
- meetingId:
|
||||
_eq: X-Hasura-MeetingId
|
||||
- userId:
|
||||
_eq: X-Hasura-UserId
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: bbb_client
|
||||
@ -25,7 +28,10 @@ update_permissions:
|
||||
columns:
|
||||
- inviteDismissedAt
|
||||
filter:
|
||||
userId:
|
||||
_eq: X-Hasura-UserId
|
||||
_and:
|
||||
- meetingId:
|
||||
_eq: X-Hasura-MeetingId
|
||||
- userId:
|
||||
_eq: X-Hasura-UserId
|
||||
check: null
|
||||
comment: ""
|
||||
|
@ -11,6 +11,7 @@ object_relationships:
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
meetingId: meetingId
|
||||
userId: userId
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
@ -25,7 +26,7 @@ select_permissions:
|
||||
- captionId
|
||||
- userId
|
||||
- captionType
|
||||
- lang
|
||||
- locale
|
||||
filter:
|
||||
meetingId:
|
||||
_eq: X-Hasura-MeetingId
|
||||
|
@ -1,17 +1,18 @@
|
||||
table:
|
||||
name: v_caption_typed_activeLocales
|
||||
name: v_caption_activeLocales
|
||||
schema: public
|
||||
configuration:
|
||||
column_config: {}
|
||||
custom_column_names: {}
|
||||
custom_name: caption_typed_activeLocales
|
||||
custom_name: caption_activeLocales
|
||||
custom_root_fields: {}
|
||||
object_relationships:
|
||||
- name: userOwner
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
userId: userId
|
||||
meetingId: meetingId
|
||||
ownerUserId: userId
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
name: v_user_ref
|
||||
@ -20,7 +21,8 @@ select_permissions:
|
||||
- role: bbb_client
|
||||
permission:
|
||||
columns:
|
||||
- lang
|
||||
- locale
|
||||
- captionType
|
||||
filter:
|
||||
meetingId:
|
||||
_eq: X-Hasura-MeetingId
|
@ -11,6 +11,7 @@ object_relationships:
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
meetingId: meetingId
|
||||
participantId: userId
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
|
@ -11,6 +11,7 @@ object_relationships:
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
meetingId: meetingId
|
||||
senderId: userId
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user