Merge pull request #20223 from antobinary/merge-may-10

Chore: Merge BBB 3.0.0-alpha.6 into develop
This commit is contained in:
Anton Georgiev 2024-05-10 20:31:11 -04:00 committed by GitHub
commit faeb84c6d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1144 changed files with 22369 additions and 28174 deletions

View File

@ -114,7 +114,7 @@ jobs:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]
env:
shard: ${{ matrix.shard }}/8
shard: ${{ matrix.shard }}/8
steps:
- uses: actions/checkout@v4
- name: Merge branches
@ -255,17 +255,22 @@ jobs:
apt --purge -y remove apache2-bin
'
- name: Install BBB
timeout-minutes: 25
run: |
sudo -i <<EOF
set -e
cd /root/ && wget -nv https://raw.githubusercontent.com/bigbluebutton/bbb-install/v3.0.x-release/bbb-install.sh -O bbb-install.sh
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v jammy-30-dev -s bbb-ci.test -j -d /certs/
bbb-conf --salt bbbci
echo "NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt" >> /usr/share/meteor/bundle/bbb-html5-with-roles.conf
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
bbb-conf --restart
EOF
uses: nick-fields/retry@v3
env:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
ACTIONS_RUNNER_DEBUG: true
with:
timeout_minutes: 25
max_attempts: 2
command: |
sudo -i <<EOF
set -e
cd /root/ && wget -nv https://raw.githubusercontent.com/bigbluebutton/bbb-install/v3.0.x-release/bbb-install.sh -O bbb-install.sh
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v jammy-30-dev -s bbb-ci.test -j -d /certs/
bbb-conf --salt bbbci
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
bbb-conf --restart
EOF
- name: List systemctl services
timeout-minutes: 1
run: |
@ -284,7 +289,7 @@ jobs:
uses: nick-fields/retry@v3
with:
timeout_minutes: 25
max_attempts: 3
max_attempts: 2
command: |
cd ./bigbluebutton-tests/playwright
npm run test-chromium-ci -- --shard ${{ env.shard }}
@ -313,7 +318,7 @@ jobs:
name: Upload blob report to GitHub Actions Artifacts
uses: actions/upload-artifact@v4
with:
name: all-blob-reports-${{ matrix.shard }}
name: blob-report-${{ matrix.shard }}
path: bigbluebutton-tests/playwright/blob-report
- if: failure()
name: Prepare artifacts (configs and logs)
@ -352,30 +357,43 @@ jobs:
name: bbb-logs-${{ env.MATRIX_SHARD }}
path: ./bbb-logs.tar.gz
upload-report:
if: always()
if: always() && !contains(github.event.head_commit.message, 'Merge pull request')
needs: install-and-run-tests
runs-on: ubuntu-latest
env:
hasReportData: ${{ needs.install-and-run-tests.result == 'success' || needs.install-and-run-tests.result == 'failure' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
if: ${{ env.hasReportData }}
working-directory: ./bigbluebutton-tests/playwright
run: npm ci
- name: Merge artifacts
uses: actions/upload-artifact/merge@v4
with:
name: all-blob-reports
pattern: blob-report-*
delete-merged: true
- name: Download all blob reports from GitHub Actions Artifacts
if: ${{ env.hasReportData }}
uses: actions/download-artifact@v4
with:
pattern: all-blob-reports-*
path: bigbluebutton-tests/playwright/all-blob-reports
merge-multiple: true
- name: Merge into HTML Report
if: ${{ env.hasReportData }}
working-directory: ./bigbluebutton-tests/playwright
run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload HTML tests report
if: ${{ env.hasReportData }}
uses: actions/upload-artifact@v4
with:
name: tests-report
overwrite: true
path: |
bigbluebutton-tests/playwright/playwright-report
bigbluebutton-tests/playwright/test-results

View File

@ -68,7 +68,6 @@ object Dependencies {
val postgresql = "org.postgresql" % "postgresql" % Versions.postgresql
val jacksonDataFormat = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % Versions.jacksonDataFormat
val snakeYaml = "org.yaml" % "snakeyaml"
}
object Test {

View File

@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory
import java.io.{ ByteArrayInputStream, File }
import scala.io.BufferedSource
import scala.util.{ Failure, Success, Try }
import scala.util.{ Failure, Success }
object ClientSettings extends SystemConfiguration {
var clientSettingsFromFile: Map[String, Object] = Map("" -> "")
@ -82,6 +82,24 @@ object ClientSettings extends SystemConfiguration {
}
}
def getConfigPropertyValueByPathAsListOfStringOrElse(map: Map[String, Any], path: String, alternativeValue: List[String]): List[String] = {
getConfigPropertyValueByPath(map, path) match {
case Some(configValue: List[String]) => configValue
case _ =>
logger.debug(s"Config `$path` with type List[String] not found in clientSettings.")
alternativeValue
}
}
def getConfigPropertyValueByPathAsListOfIntOrElse(map: Map[String, Any], path: String, alternativeValue: List[Int]): List[Int] = {
getConfigPropertyValueByPath(map, path) match {
case Some(configValue: List[Int]) => configValue
case _ =>
logger.debug(s"Config `$path` with type List[Int] not found in clientSettings.")
alternativeValue
}
}
def getConfigPropertyValueByPath(map: Map[String, Any], path: String): Option[Any] = {
val keys = path.split("\\.")

View File

@ -154,7 +154,7 @@ class BigBlueButtonActor(
}
private def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = {
RunningMeetings.meetings(meetings).filter(_.props.systemProps.html5InstanceId == msg.body.html5InstanceId).foreach(m => {
RunningMeetings.meetings(meetings).foreach(m => {
m.actorRef ! msg
})
}

View File

@ -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
@ -138,10 +131,3 @@ case class UserClosedAllGraphqlConnectionsInternalMsg(userId: String) extends In
* @param userId
*/
case class UserEstablishedGraphqlConnectionInternalMsg(userId: String) extends InMessage
// DeskShare
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String) extends InMessage

View File

@ -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]()
@ -70,7 +70,6 @@ class WhiteboardModel extends SystemConfiguration {
val newAnnotation = oldAnnotation.get.copy(annotationInfo = finalAnnotationInfo)
newAnnotationsMap += (annotation.id -> newAnnotation)
annotationsAdded :+= newAnnotation
PresAnnotationDAO.insertOrUpdate(newAnnotation, newAnnotation)
println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
} else {
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
@ -78,13 +77,14 @@ class WhiteboardModel extends SystemConfiguration {
} else if (annotation.annotationInfo.contains("type")) {
newAnnotationsMap += (annotation.id -> annotation)
annotationsAdded :+= annotation
PresAnnotationDAO.insertOrUpdate(annotation, annotation)
println(s"Adding annotation to page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
} else {
println(s"New annotation [${annotation.id}] with no type, ignoring...")
}
}
PresAnnotationDAO.insertOrUpdateMap(meetingId, annotationsAdded)
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(newWb)
annotationsAdded
@ -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)
annotationsIdsRemoved.map(PresAnnotationDAO.delete(wbId, userId, _))
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)
}

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.core.api.BreakoutRoomEndedInternalMsg
import org.bigbluebutton.core.db.BreakoutRoomDAO
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, NotificationDAO }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.MsgBuilder
@ -37,6 +37,7 @@ trait BreakoutRoomEndedInternalMsgHdlr {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
BreakoutRoomDAO.updateRoomsEnded(liveMeeting.props.meetingProp.intId)
state.update(None)

View File

@ -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)
}

View File

@ -5,7 +5,7 @@ import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers.getRedirectUrls
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.BreakoutRoomUserDAO
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, NotificationDAO}
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.EjectReasonCode
import org.bigbluebutton.core.running.{MeetingActor, OutMsgRouter}
@ -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
@ -75,6 +75,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
Vector(roomTo.shortName)
)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
}
}

View File

@ -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)

View File

@ -6,9 +6,8 @@ import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.domain.{ MeetingEndReason, MeetingState2x }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.UserBreakoutRoomDAO
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, NotificationDAO, UserBreakoutRoomDAO }
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.db.BreakoutRoomDAO
trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
@ -22,24 +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)
}
BreakoutRoomDAO.updateRoomsEnded(meetingId)
state.update(None)
}

View File

@ -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)
}
}

View File

@ -1,13 +1,14 @@
package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ SendMessageToBreakoutRoomInternalMsg }
import org.bigbluebutton.core.api.SendMessageToBreakoutRoomInternalMsg
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ RegisteredUsers }
import org.bigbluebutton.core.models.RegisteredUsers
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
@ -32,7 +33,7 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
val event = buildSendMessageToAllBreakoutRoomsEvtMsg(msg.header.userId, msg.body.msg, breakoutModel.rooms.size)
outGW.send(event)
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
msg.header.userId,
liveMeeting.props.meetingProp.intId,
"info",
@ -41,7 +42,8 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
"Message for chat sent successfully",
Vector(s"${breakoutModel.rooms.size}")
)
outGW.send(notifyModeratorEvent)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
log.debug("Sending message '{}' to all breakout rooms in meeting {}", msg.body.msg, props.meetingProp.intId)
}

View File

@ -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)
}

View File

@ -4,7 +4,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ SendTimeRemainingAuditInternalMsg, UpdateBreakoutRoomTimeInternalMsg }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO }
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO, NotificationDAO }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
@ -63,9 +63,10 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
Vector(s"${msg.body.timeInMinutes}")
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
msg.header.userId,
liveMeeting.props.meetingProp.intId,
"info",
@ -74,7 +75,8 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
"Sent to the moderator that requested breakout duration change",
Vector(s"${msg.body.timeInMinutes}")
)
outGW.send(notifyModeratorEvent)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
BreakoutRoomDAO.updateRoomsDuration(props.meetingProp.intId, newDurationInSeconds)

View File

@ -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)) {

View File

@ -11,7 +11,7 @@ trait SyncGetGroupChatsInfoMsgHdlr {
def handleSyncGetGroupChatsInfo(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetGroupChatsRespMsg(allChats: Vector[GroupChatInfo]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatsRespMsgBody(allChats)
@ -21,7 +21,7 @@ trait SyncGetGroupChatsInfoMsgHdlr {
}
def buildSyncGetGroupChatMsgsRespMsg(msgs: Vector[GroupChatMsgToUser], chatId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatMsgsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatMsgsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatMsgsRespMsgBody(chatId, msgs)

View File

@ -5,8 +5,8 @@ import org.bigbluebutton.core.models.{ Layouts, LayoutsType }
import org.bigbluebutton.core.running.OutMsgRouter
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.LayoutDAO
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
import org.bigbluebutton.core.db.{ LayoutDAO, NotificationDAO }
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
this: LayoutApp2x =>
@ -73,6 +73,7 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
}

View File

@ -9,7 +9,7 @@ trait SyncGetMeetingInfoRespMsgHdlr {
val outGW: OutMsgRouter
def handleSyncGetMeetingInfoRespMsg(props: DefaultProps): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(props.meetingProp.intId, props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetMeetingInfoRespMsg.NAME, routing)
val header = BbbCoreBaseHeader(SyncGetMeetingInfoRespMsg.NAME)

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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 _ =>
}

View File

@ -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 _ =>

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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
)
}
}

View File

@ -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,

View File

@ -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
)
}
}

View File

@ -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)

View File

@ -7,8 +7,8 @@ import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.{ ChatMessageDAO, JsonUtils }
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
import org.bigbluebutton.core.db.{ChatMessageDAO, JsonUtils, NotificationDAO}
import org.bigbluebutton.core2.message.senders.MsgBuilder
import spray.json.DefaultJsonProtocol.jsonFormat2
@ -37,6 +37,7 @@ trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
Vector()
)
bus.outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
// SendWhiteboardAnnotationPubMsg
val annotationRouting = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -12,7 +12,7 @@ trait SyncGetPresentationPodsMsgHdlr {
def handleSyncGetPresentationPods(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetPresentationPodsRespMsg(pods: Vector[PresentationPodVO]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetPresentationPodsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetPresentationPodsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")

View File

@ -9,9 +9,10 @@ trait SyncGetScreenshareInfoRespMsgHdlr {
this: ScreenshareApp2x =>
def handleSyncGetScreenshareInfoRespMsg(liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId,
liveMeeting.props.systemProps.html5InstanceId.toString
"nodeJSapp"
)
val envelope = BbbCoreEnvelope(SyncGetScreenshareInfoRespMsg.NAME, routing)
val header = BbbClientMsgHeader(

View File

@ -3,7 +3,7 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.MeetingLockSettingsDAO
import org.bigbluebutton.core.db.{ MeetingLockSettingsDAO, NotificationDAO }
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.OutMsgRouter
import org.bigbluebutton.core.running.MeetingActor
@ -66,6 +66,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
LockSettingsUtil.enforceCamLockSettingsForAllUsers(liveMeeting, outGW)
} else {
@ -78,6 +79,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -92,6 +94,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
if (vu.intId.startsWith(IntIdPrefixType.DIAL_IN)) { // only Dial-in users need this
val eventExplicitLock = buildLockMessage(liveMeeting.props.meetingProp.intId, vu.intId, msg.body.setBy, settings.disableMic)
@ -109,6 +112,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -123,6 +127,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -133,6 +138,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -147,6 +153,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -157,6 +164,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -171,6 +179,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -181,6 +190,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -195,6 +205,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -205,6 +216,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}

View File

@ -5,6 +5,7 @@ import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, UserState, Users2
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
@ -43,6 +44,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
@ -59,6 +61,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
val newUvo: UserState = Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,

View File

@ -1,6 +1,7 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.util.ColorPicker
@ -58,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)
@ -107,6 +108,7 @@ trait RegisterUserReqMsgHdlr {
Vector(s"${regUser.name}")
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
case GuestStatus.DENY =>
val g = GuestApprovedVO(regUser.id, GuestStatus.DENY)
UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID)

View File

@ -10,7 +10,7 @@ import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.db.MeetingRecordingDAO
import org.bigbluebutton.core.db.{ MeetingRecordingDAO, NotificationDAO }
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
@ -49,6 +49,7 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
MeetingStatus2x.recordingStarted(liveMeeting.status)
MeetingRecordingDAO.insertRecording(liveMeeting.props.meetingProp.intId, msg.body.setBy)
@ -75,6 +76,7 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
MeetingStatus2x.recordingStopped(liveMeeting.status)
MeetingRecordingDAO.updateStopped(liveMeeting.props.meetingProp.intId, msg.body.setBy)

View File

@ -11,7 +11,7 @@ trait SyncGetUsersMeetingRespMsgHdlr {
val outGW: OutMsgRouter
def handleSyncGetUsersMeetingRespMsg(): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetUsersMeetingRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetUsersMeetingRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")

View File

@ -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,

View File

@ -1,9 +1,10 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPathAsListOfIntOrElse, getConfigPropertyValueByPathAsListOfStringOrElse }
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.RightsManagementTrait
import org.bigbluebutton.core.db.UserConnectionStatusDAO
import org.bigbluebutton.core.models.{ UserState, Users2x }
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait {
@ -13,13 +14,42 @@ trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait {
val outGW: OutMsgRouter
def handleUserConnectionAliveReqMsg(msg: UserConnectionAliveReqMsg): Unit = {
log.info("handleUserConnectionAliveReqMsg: userId={}", msg.body.userId)
log.info("handleUserConnectionAliveReqMsg: networkRttInMs={} userId={}", msg.body.networkRttInMs, msg.body.userId)
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
UserConnectionStatusDAO.updateUserAlive(user.intId)
val rtt: Option[Double] = msg.body.networkRttInMs match {
case 0 => None
case rtt: Double => Some(rtt)
}
val status = getLevelFromRtt(msg.body.networkRttInMs)
UserConnectionStatusDAO.updateUserAlive(user.meetingId, user.intId, rtt, status)
}
}
def getLevelFromRtt(networkRttInMs: Double): String = {
val levelOptions = getConfigPropertyValueByPathAsListOfStringOrElse(
liveMeeting.clientSettings,
"public.stats.level",
List("warning", "danger", "critical")
)
val rttOptions = getConfigPropertyValueByPathAsListOfIntOrElse(
liveMeeting.clientSettings,
"public.stats.rtt",
List(500, 1000, 2000)
)
val statusRttXLevel = levelOptions.zip(rttOptions).reverse
val statusFound = statusRttXLevel.collectFirst {
case (level, rtt) if networkRttInMs > rtt => level
}
statusFound.getOrElse("normal")
}
}

View File

@ -1,25 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.RightsManagementTrait
import org.bigbluebutton.core.db.UserConnectionStatusDAO
import org.bigbluebutton.core.models.{ UserState, Users2x }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
trait UserConnectionUpdateRttReqMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleUserConnectionUpdateRttReqMsg(msg: UserConnectionUpdateRttReqMsg): Unit = {
log.info("handleUserConnectionUpdateRttReqMsg: networkRttInMs={} userId={}", msg.body.networkRttInMs, msg.body.userId)
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
UserConnectionStatusDAO.updateUserRtt(user.intId, msg.body.networkRttInMs)
}
}
}

View File

@ -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)
}
}

View File

@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
import org.bigbluebutton.core.db.{ UserDAO, UserStateDAO }
import org.bigbluebutton.core.db.{ NotificationDAO, UserDAO, UserStateDAO }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running._
@ -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
}
@ -137,6 +137,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
Vector(newUser.name)
)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
}
private def clearCachedVoiceUser(regUser: RegisteredUser) =
@ -144,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)

View File

@ -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 {
@ -169,7 +169,6 @@ class UsersApp(
with ChangeUserPinStateReqMsgHdlr
with ChangeUserMobileFlagReqMsgHdlr
with UserConnectionAliveReqMsgHdlr
with UserConnectionUpdateRttReqMsgHdlr
with ChangeUserReactionEmojiReqMsgHdlr
with ChangeUserRaiseHandReqMsgHdlr
with ChangeUserAwayReqMsgHdlr

View File

@ -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)

View File

@ -17,7 +17,7 @@ trait SyncGetVoiceUsersMsgHdlr {
callerNum = u.callerNum, color = u.color, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
}
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetVoiceUsersRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetVoiceUsersRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetVoiceUsersRespMsgBody(voiceUsers)

View File

@ -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,

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -13,9 +13,10 @@ trait SyncGetWebcamInfoRespMsgHdlr {
this: WebcamApp2x =>
def handleSyncGetWebcamInfoRespMsg(liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId,
liveMeeting.props.systemProps.html5InstanceId.toString
"nodeJSapp"
)
val envelope = BbbCoreEnvelope(SyncGetWebcamInfoRespMsg.NAME, routing)
val header = BbbClientMsgHeader(

View File

@ -3,7 +3,7 @@ package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.db.MeetingUsersPoliciesDAO
import org.bigbluebutton.core.db.{ MeetingUsersPoliciesDAO, NotificationDAO }
import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, Users2x }
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
@ -64,6 +64,7 @@ trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
Vector()
)
bus.outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
meetingId,
@ -74,6 +75,7 @@ trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
Vector()
)
bus.outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
broadcastEvent(meetingId, msg.body.setBy, value)

View File

@ -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)
}
}

View File

@ -11,7 +11,7 @@ trait ClearWhiteboardPubMsgHdlr extends RightsManagementTrait {
def handle(msg: ClearWhiteboardPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: ClearWhiteboardPubMsg, fullClear: Boolean): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(ClearWhiteboardEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ClearWhiteboardEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -11,7 +11,7 @@ trait DeleteWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
def handle(msg: DeleteWhiteboardAnnotationsPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: DeleteWhiteboardAnnotationsPubMsg, removedAnnotationsIds: Array[String]): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(DeleteWhiteboardAnnotationsEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(DeleteWhiteboardAnnotationsEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -10,7 +10,7 @@ trait GetWhiteboardAnnotationsReqMsgHdlr {
def handle(msg: GetWhiteboardAnnotationsReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: GetWhiteboardAnnotationsReqMsg, history: Array[AnnotationVO], multiUser: Array[String]): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(GetWhiteboardAnnotationsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetWhiteboardAnnotationsRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -11,7 +11,7 @@ trait ModifyWhiteboardAccessPubMsgHdlr extends RightsManagementTrait {
def handle(msg: ModifyWhiteboardAccessPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: ModifyWhiteboardAccessPubMsg): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(ModifyWhiteboardAccessEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ModifyWhiteboardAccessEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -12,7 +12,7 @@ trait SendCursorPositionPubMsgHdlr extends RightsManagementTrait {
def handle(msg: SendCursorPositionPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: SendCursorPositionPubMsg): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(SendCursorPositionEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SendCursorPositionEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
@ -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)
}
}
}

View File

@ -10,8 +10,8 @@ trait SendWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
def handle(msg: SendWhiteboardAnnotationsPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: SendWhiteboardAnnotationsPubMsg, whiteboardId: String, annotations: Array[AnnotationVO], html5InstanceId: String): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, html5InstanceId)
def broadcastEvent(msg: SendWhiteboardAnnotationsPubMsg, whiteboardId: String, annotations: Array[AnnotationVO]): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(SendWhiteboardAnnotationsEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SendWhiteboardAnnotationsEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
@ -58,7 +58,7 @@ trait SendWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
// }
// println("============= Printed Sanitized annotations ============")
val annotations = sendWhiteboardAnnotations(msg.body.whiteboardId, msg.header.userId, msg.body.annotations, liveMeeting, isUserAmongPresenters, isUserModerator)
broadcastEvent(msg, msg.body.whiteboardId, annotations, msg.body.html5InstanceId)
broadcastEvent(msg, msg.body.whiteboardId, annotations)
} else {
//val meetingId = liveMeeting.props.meetingProp.intId
//val reason = "No permission to send a whiteboard annotation."

View File

@ -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 = {

View File

@ -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 {

View File

@ -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())))

View File

@ -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

View 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")
}
}
}

View File

@ -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))
}

View File

@ -0,0 +1,76 @@
package org.bigbluebutton.core.db
import org.bigbluebutton.common2.msgs.{BbbCommonEnvCoreMsg, NotifyAllInMeetingEvtMsg, NotifyRoleInMeetingEvtMsg, NotifyUserInMeetingEvtMsg}
import PostgresProfile.api._
import spray.json.JsValue
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
case class NotificationDbModel(
// notificationId: String,
meetingId: String,
notificationType: String,
icon: String,
messageId: String,
messageDescription: String,
messageValues: JsValue,
role: Option[String],
userMeetingId: Option[String],
userId: Option[String],
createdAt: java.sql.Timestamp,
)
class NotificationDbTableDef(tag: Tag) extends Table[NotificationDbModel](tag, None, "notification") {
val meetingId = column[String]("meetingId")
val notificationType = column[String]("notificationType")
val icon = column[String]("icon")
val messageId = column[String]("messageId")
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, userMeetingId, userId, createdAt) <> (NotificationDbModel.tupled, NotificationDbModel.unapply)
}
object NotificationDAO {
def insert(notification: BbbCommonEnvCoreMsg) = {
val (meetingId, notificationType, icon, messageId, messageDescription, messageValues, role, userId) = notification.core match {
case event: NotifyAllInMeetingEvtMsg =>
(event.body.meetingId, event.body.notificationType, event.body.icon, event.body.messageId, event.body.messageDescription, event.body.messageValues, None, None)
case event: NotifyRoleInMeetingEvtMsg =>
(event.body.meetingId, event.body.notificationType, event.body.icon, event.body.messageId, event.body.messageDescription, event.body.messageValues, Some(event.body.role), None)
case event: NotifyUserInMeetingEvtMsg =>
(event.body.meetingId, event.body.notificationType, event.body.icon, event.body.messageId, event.body.messageDescription, event.body.messageValues, None, Some(event.body.userId))
case _ =>
("","", "", "", "", Vector(""), None, None)
}
if (notificationType != "") {
DatabaseConnection.db.run(
TableQuery[NotificationDbTableDef].forceInsert(
NotificationDbModel(
meetingId,
notificationType,
icon,
messageId,
messageDescription,
JsonUtils.vectorToJson(messageValues),
role,
userMeetingId = userId match {
case Some(u) => Some(meetingId)
case _ => None
},
userId,
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
)
)
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted/updated on Notification table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting/updating Notification: $e")
}
}
}
}

View File

@ -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,54 +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 "meetingId" = ${meetingId}
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")
}
}

View File

@ -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)
)
)

View File

@ -3,6 +3,7 @@ package org.bigbluebutton.core.db
import com.github.tminglei.slickpg._
import org.apache.pekko.http.scaladsl.model.ParsingException
import org.bigbluebutton.common2.domain.SimpleVoteOutVO
import spray.json.DefaultJsonProtocol.{ StringJsonFormat, vectorFormat }
import spray.json.{ JsArray, JsBoolean, JsNumber, JsObject, JsString, JsValue, JsonWriter, _ }
import scala.util.{ Failure, Success, Try }
@ -55,6 +56,10 @@ object JsonUtils {
genericMap.toJson
}
def vectorToJson(genericVector: Vector[String]) = {
genericVector.toJson
}
def stringToJson(jsonString: String): JsValue = {
Try(jsonString.parseJson) match {
case Success(jsValue) => jsValue

View File

@ -1,13 +1,14 @@
package org.bigbluebutton.core.db
import org.bigbluebutton.common2.msgs.AnnotationVO
import PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success }
import slick.jdbc.PostgresProfile.api._
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),
@ -42,8 +45,8 @@ object PresAnnotationDAO {
)
)
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PresAnnotation table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PresAnnotation: $e")
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted or updated on PresAnnotation table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting or updating PresAnnotation: $e")
}
}
@ -51,15 +54,44 @@ object PresAnnotationDAO {
}
}
def delete(wbId: String, userId: String, annotationId: String) = {
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,
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis())
)
)
}
PresAnnotationHistoryDAO.delete(wbId, userId, annotationId).onComplete {
def insertOrUpdateMap(meetingId: String, annotations: Array[AnnotationVO]) = {
DatabaseConnection.db.run(
DBIO.sequence(
annotations.map { annotation =>
prepareInsertOrUpdate(meetingId, annotation)
}.toVector
).transactionally
)
.onComplete {
case Success(rowsAffected) =>
DatabaseConnection.logger.debug(s"${rowsAffected.sum} row(s) inserted or updated on PresAnnotation table!")
case Failure(e) =>
DatabaseConnection.logger.debug(s"Error inserting or updating PresAnnotation: $e")
}
}
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")
@ -69,4 +101,16 @@ object PresAnnotationDAO {
}
}
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.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")
}
}
}

View File

@ -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 = ""
)

View File

@ -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,

View File

@ -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
)

View File

@ -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 {

View File

@ -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) = {

View File

@ -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,

View File

@ -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 {

View File

@ -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)

View File

@ -5,22 +5,24 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success }
case class UserConnectionStatusDbModel(
userId: String,
meetingId: String,
connectionAliveAt: Option[java.sql.Timestamp],
userClientResponseAt: Option[java.sql.Timestamp],
networkRttInMs: Option[Double]
userId: String,
meetingId: String,
connectionAliveAt: Option[java.sql.Timestamp],
networkRttInMs: Option[Double],
status: String,
statusUpdatedAt: Option[java.sql.Timestamp]
)
class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatusDbModel](tag, None, "user_connectionStatus") {
override def * = (
userId, meetingId, connectionAliveAt, userClientResponseAt, networkRttInMs
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 userClientResponseAt = column[Option[java.sql.Timestamp]]("userClientResponseAt")
val networkRttInMs = column[Option[Double]]("networkRttInMs")
val status = column[String]("status")
val statusUpdatedAt = column[Option[java.sql.Timestamp]]("statusUpdatedAt")
}
object UserConnectionStatusDAO {
@ -32,8 +34,9 @@ object UserConnectionStatusDAO {
userId = userId,
meetingId = meetingId,
connectionAliveAt = None,
userClientResponseAt = None,
networkRttInMs = None
networkRttInMs = None,
status = "normal",
statusUpdatedAt = None
)
)
).onComplete {
@ -42,28 +45,24 @@ object UserConnectionStatusDAO {
}
}
def updateUserAlive(userId: 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))
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
.map(t => (t.connectionAliveAt, t.networkRttInMs, t.status, t.statusUpdatedAt))
.update(
(
Some(new java.sql.Timestamp(System.currentTimeMillis())),
rtt,
status,
Some(new java.sql.Timestamp(System.currentTimeMillis())),
)
)
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated connectionAliveAt on UserConnectionStatus table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating connectionAliveAt on UserConnectionStatus: $e")
}
}
def updateUserRtt(userId: String, networkRttInMs: Double) = {
DatabaseConnection.db.run(
TableQuery[UserConnectionStatusDbTableDef]
.filter(_.userId === userId)
.map(t => (t.networkRttInMs, t.userClientResponseAt))
.update((Some(networkRttInMs), Some(new java.sql.Timestamp(System.currentTimeMillis()))))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated networkRttInMs on UserConnectionStatus table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating networkRttInMs on UserConnectionStatus: $e")
}
}
}

View File

@ -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

View File

@ -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,18 +115,20 @@ 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))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user voice table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user voice: $e")
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user table: $e")
}
}
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]

View File

@ -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,

View File

@ -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 {

View File

@ -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")
}
}

View File

@ -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")
}
}

View File

@ -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))

View File

@ -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")
}
}
}
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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
@ -113,8 +114,6 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[ChangeUserMobileFlagReqMsg](envelope, jsonNode)
case UserConnectionAliveReqMsg.NAME =>
routeGenericMsg[UserConnectionAliveReqMsg](envelope, jsonNode)
case UserConnectionUpdateRttReqMsg.NAME =>
routeGenericMsg[UserConnectionUpdateRttReqMsg](envelope, jsonNode)
case SetUserSpeechLocaleReqMsg.NAME =>
routeGenericMsg[SetUserSpeechLocaleReqMsg](envelope, jsonNode)
case SetUserSpeechOptionsReqMsg.NAME =>
@ -181,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)
@ -422,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)
@ -472,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
}
}

View File

@ -1,24 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.core.record.events
trait AbstractDeskshareRecordEvent extends RecordEvent {
setModule("DESKSHARE")
}

View File

@ -1,34 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.core.record.events
class DeskshareStartRtmpRecordEvent extends AbstractDeskshareRecordEvent {
import DeskshareStartRtmpRecordEvent._
setEvent("DeskShareStartRTMP")
def setStreamPath(streamPath: String) {
eventMap.put(STREAM_PATH, streamPath)
}
}
object DeskshareStartRtmpRecordEvent {
protected final val STREAM_PATH = "startIstreamPathndex"
}

View File

@ -1,34 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.core.record.events
class DeskshareStopRtmpRecordEvent extends AbstractDeskshareRecordEvent {
import DeskshareStopRtmpRecordEvent._
setEvent("DeskShareStopRTMP")
def setStreamPath(streamPath: String) {
eventMap.put(STREAM_PATH, streamPath)
}
}
object DeskshareStopRtmpRecordEvent {
protected final val STREAM_PATH = "startIstreamPathndex"
}

View File

@ -7,7 +7,7 @@ import org.bigbluebutton.core.apps.groupchats.GroupChatApp
import org.bigbluebutton.core.apps.users.UsersApp
import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.bus.{BigBlueButtonEvent, InternalEventBus}
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, MeetingDAO, MeetingRecordingDAO, UserBreakoutRoomDAO}
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, MeetingDAO, MeetingRecordingDAO, NotificationDAO, UserBreakoutRoomDAO}
import org.bigbluebutton.core.domain.{MeetingEndReason, MeetingState2x}
import org.bigbluebutton.core.models._
import org.bigbluebutton.core2.MeetingStatus2x
@ -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,
@ -101,6 +102,7 @@ trait HandlerHelpers extends SystemConfiguration {
Vector(s"${newUser.name}")
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
val newState = startRecordingIfAutoStart2x(outGW, liveMeeting, state)
if (!Users2x.hasPresenter(liveMeeting.users2x)) {
@ -236,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)
}
}

View File

@ -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.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)
@ -405,7 +431,6 @@ class MeetingActor(
case m: ChangeUserPinStateReqMsg => usersApp.handleChangeUserPinStateReqMsg(m)
case m: ChangeUserMobileFlagReqMsg => usersApp.handleChangeUserMobileFlagReqMsg(m)
case m: UserConnectionAliveReqMsg => usersApp.handleUserConnectionAliveReqMsg(m)
case m: UserConnectionUpdateRttReqMsg => usersApp.handleUserConnectionUpdateRttReqMsg(m)
case m: SetUserSpeechLocaleReqMsg => usersApp.handleSetUserSpeechLocaleReqMsg(m)
case m: SetUserSpeechOptionsReqMsg => usersApp.handleSetUserSpeechOptionsReqMsg(m)
@ -531,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)
@ -604,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
@ -925,6 +949,7 @@ class MeetingActor(
Vector(s"${u.name}")
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
if (u.presenter) {
log.info("removeUsersWithExpiredUserLeftFlag will cause an automaticallyAssignPresenter because user={} left", u)
@ -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)
}
}

Some files were not shown because too many files have changed in this diff Show More